From f312e00dfaea78a163af45734c2c1b91d1d85bba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garc=C3=ADa?= Date: Sat, 9 Nov 2024 16:53:10 +0100 Subject: [PATCH 001/286] Test dylint --- dylint.toml | 2 + dylints/README.md | 7 + .../.cargo/config.toml | 2 + dylints/non_authenticated_routes/.gitignore | 1 + dylints/non_authenticated_routes/Cargo.lock | 1659 +++++++++++++++++ dylints/non_authenticated_routes/Cargo.toml | 20 + .../non_authenticated_routes/rust-toolchain | 3 + dylints/non_authenticated_routes/src/lib.rs | 167 ++ dylints/non_authenticated_routes/ui/main.rs | 1 + .../non_authenticated_routes/ui/main.stderr | 0 10 files changed, 1862 insertions(+) create mode 100644 dylint.toml create mode 100644 dylints/README.md create mode 100644 dylints/non_authenticated_routes/.cargo/config.toml create mode 100644 dylints/non_authenticated_routes/.gitignore create mode 100644 dylints/non_authenticated_routes/Cargo.lock create mode 100644 dylints/non_authenticated_routes/Cargo.toml create mode 100644 dylints/non_authenticated_routes/rust-toolchain create mode 100644 dylints/non_authenticated_routes/src/lib.rs create mode 100644 dylints/non_authenticated_routes/ui/main.rs create mode 100644 dylints/non_authenticated_routes/ui/main.stderr diff --git a/dylint.toml b/dylint.toml new file mode 100644 index 00000000..021963a9 --- /dev/null +++ b/dylint.toml @@ -0,0 +1,2 @@ +[workspace.metadata.dylint] +libraries = [{ path = "dylints/*" }] diff --git a/dylints/README.md b/dylints/README.md new file mode 100644 index 00000000..996d2a85 --- /dev/null +++ b/dylints/README.md @@ -0,0 +1,7 @@ +# How to run Lints + +```sh +cargo install cargo-dylint dylint-link + +RUSTFLAGS="-Aunreachable_patterns" cargo dylint --all -- --features sqlite +``` \ No newline at end of file diff --git a/dylints/non_authenticated_routes/.cargo/config.toml b/dylints/non_authenticated_routes/.cargo/config.toml new file mode 100644 index 00000000..93dceb69 --- /dev/null +++ b/dylints/non_authenticated_routes/.cargo/config.toml @@ -0,0 +1,2 @@ +[target.'cfg(all())'] +linker = "dylint-link" diff --git a/dylints/non_authenticated_routes/.gitignore b/dylints/non_authenticated_routes/.gitignore new file mode 100644 index 00000000..ea8c4bf7 --- /dev/null +++ b/dylints/non_authenticated_routes/.gitignore @@ -0,0 +1 @@ +/target diff --git a/dylints/non_authenticated_routes/Cargo.lock b/dylints/non_authenticated_routes/Cargo.lock new file mode 100644 index 00000000..e2501d54 --- /dev/null +++ b/dylints/non_authenticated_routes/Cargo.lock @@ -0,0 +1,1659 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi", +] + +[[package]] +name = "anstream" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" + +[[package]] +name = "anstyle-parse" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" +dependencies = [ + "anstyle", + "windows-sys 0.59.0", +] + +[[package]] +name = "anyhow" +version = "1.0.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775" + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bstr" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40723b8fb387abc38f4f4a37c09073622e41dd12327033091ef8950659e6dc0c" +dependencies = [ + "memchr", + "serde", +] + +[[package]] +name = "camino" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b96ec4966b5813e2c0507c1f86115c8c5abaadc3980879c3424042a02fd1ad3" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo-platform" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24b1f0365a6c6bb4020cd05806fd0d33c44d38046b8bd7f0e40814b9763cabfc" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo_metadata" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037" +dependencies = [ + "camino", + "cargo-platform", + "semver", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "cc" +version = "1.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40545c26d092346d8a8dab71ee48e7685a7a9cba76e634790c215b41a4a7b4cf" +dependencies = [ + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clippy_config" +version = "0.1.84" +source = "git+https://github.com/rust-lang/rust-clippy?rev=4f0e46b74dbc8441daf084b6f141a7fe414672a2#4f0e46b74dbc8441daf084b6f141a7fe414672a2" +dependencies = [ + "itertools", + "serde", + "toml 0.7.8", +] + +[[package]] +name = "clippy_utils" +version = "0.1.84" +source = "git+https://github.com/rust-lang/rust-clippy?rev=4f0e46b74dbc8441daf084b6f141a7fe414672a2#4f0e46b74dbc8441daf084b6f141a7fe414672a2" +dependencies = [ + "arrayvec", + "clippy_config", + "itertools", + "rustc_apfloat", +] + +[[package]] +name = "colorchoice" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" + +[[package]] +name = "compiletest_rs" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71fcc3c0c91b59c137b3cf8073cbc2f72a49b3d5505660ec88f94da3ed4bb1de" +dependencies = [ + "diff", + "filetime", + "getopts", + "lazy_static", + "libc", + "log", + "miow", + "regex", + "rustfix", + "serde", + "serde_derive", + "serde_json", + "tester", + "windows-sys 0.59.0", +] + +[[package]] +name = "cpufeatures" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "diff" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "dirs" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +dependencies = [ + "cfg-if", + "dirs-sys-next", +] + +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.48.0", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "dylint" +version = "3.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22752a1003c06a3f04f9ea66e4899f97132a1888276fee2d5fbe5f6820eee274" +dependencies = [ + "ansi_term", + "anyhow", + "cargo_metadata", + "dirs", + "dylint_internal", + "is-terminal", + "log", + "once_cell", + "semver", + "serde", + "serde_json", + "tempfile", + "walkdir", +] + +[[package]] +name = "dylint_internal" +version = "3.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f02176c2fa705973bfce833c3f12a69196959086c776ec967a5aa23b5523d4b6" +dependencies = [ + "ansi_term", + "anyhow", + "bitflags 2.6.0", + "cargo_metadata", + "git2", + "home", + "if_chain", + "is-terminal", + "log", + "once_cell", + "regex", + "rust-embed", + "serde", + "thiserror", + "toml 0.8.19", +] + +[[package]] +name = "dylint_linting" +version = "3.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0338d94b92ffeece90a8ba405c6d051f18b4483558728537a333daa3bb422616" +dependencies = [ + "cargo_metadata", + "dylint_internal", + "paste", + "rustversion", + "serde", + "thiserror", + "toml 0.8.19", +] + +[[package]] +name = "dylint_testing" +version = "3.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1581603a3fc49b8ede8bed50f5e2d719ce607572f5e5d5d06f200f3285448a" +dependencies = [ + "anyhow", + "cargo_metadata", + "compiletest_rs", + "dylint", + "dylint_internal", + "env_logger", + "once_cell", + "regex", + "serde_json", + "tempfile", +] + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + +[[package]] +name = "env_filter" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f2c92ceda6ceec50f43169f9ee8424fe2db276791afde7b2cd8bc084cb376ab" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "env_logger" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13fa619b91fb2381732789fc5de83b45675e882f66623b7d8cb4f643017018d" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "humantime", + "log", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "fastrand" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4" + +[[package]] +name = "filetime" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586" +dependencies = [ + "cfg-if", + "libc", + "libredox", + "windows-sys 0.59.0", +] + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getopts" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "git2" +version = "0.18.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "232e6a7bfe35766bf715e55a88b39a700596c0ccfd88cd3680b4cdb40d66ef70" +dependencies = [ + "bitflags 2.6.0", + "libc", + "libgit2-sys", + "log", + "openssl-probe", + "openssl-sys", + "url", +] + +[[package]] +name = "globset" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15f1ce686646e7f1e19bf7d5533fe443a45dbfb990e00629110797578b42fb19" +dependencies = [ + "aho-corasick", + "bstr", + "log", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "hashbrown" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3" + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "hermit-abi" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" + +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "icu_collections" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" + +[[package]] +name = "icu_properties" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "idna" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "if_chain" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb56e1aa765b4b4f3aadfab769793b7087bb03a4ea4920644a6d238e2df5b9ed" + +[[package]] +name = "indexmap" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "is-terminal" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b" +dependencies = [ + "hermit-abi 0.4.0", + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "jobserver" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" +dependencies = [ + "libc", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.162" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398" + +[[package]] +name = "libgit2-sys" +version = "0.16.2+1.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee4126d8b4ee5c9d9ea891dd875cfdc1e9d0950437179104b183d7d8a74d24e8" +dependencies = [ + "cc", + "libc", + "libssh2-sys", + "libz-sys", + "openssl-sys", + "pkg-config", +] + +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags 2.6.0", + "libc", + "redox_syscall", +] + +[[package]] +name = "libssh2-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dc8a030b787e2119a731f1951d6a773e2280c660f8ec4b0f5e1505a386e71ee" +dependencies = [ + "cc", + "libc", + "libz-sys", + "openssl-sys", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "libz-sys" +version = "1.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2d16453e800a8cf6dd2fc3eb4bc99b786a9b90c663b8559a5b1a041bf89e472" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + +[[package]] +name = "litemap" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "643cb0b8d4fcc284004d5fd0d67ccf61dfffadb7f75e1e71bc420f4688a3a704" + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "miow" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "359f76430b20a79f9e20e115b3428614e654f04fab314482fc0fda0ebd3c6044" +dependencies = [ + "windows-sys 0.48.0", +] + +[[package]] +name = "non_authenticated_routes" +version = "0.1.0" +dependencies = [ + "clippy_utils", + "dylint_linting", + "dylint_testing", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi 0.3.9", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45abf306cbf99debc8195b66b7346498d7b10c210de50418b5ccd7ceba08c741" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pin-project-lite" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" + +[[package]] +name = "pkg-config" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" + +[[package]] +name = "proc-macro2" +version = "1.0.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" +dependencies = [ + "bitflags 2.6.0", +] + +[[package]] +name = "redox_users" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +dependencies = [ + "getrandom", + "libredox", + "thiserror", +] + +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "rust-embed" +version = "8.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa66af4a4fdd5e7ebc276f115e895611a34739a9c1c01028383d612d550953c0" +dependencies = [ + "rust-embed-impl", + "rust-embed-utils", + "walkdir", +] + +[[package]] +name = "rust-embed-impl" +version = "8.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6125dbc8867951125eec87294137f4e9c2c96566e61bf72c45095a7c77761478" +dependencies = [ + "proc-macro2", + "quote", + "rust-embed-utils", + "syn", + "walkdir", +] + +[[package]] +name = "rust-embed-utils" +version = "8.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e5347777e9aacb56039b0e1f28785929a8a3b709e87482e7442c72e7c12529d" +dependencies = [ + "globset", + "sha2", + "walkdir", +] + +[[package]] +name = "rustc_apfloat" +version = "0.2.1+llvm-462a31f5a5ab" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "886d94c63c812a8037c4faca2607453a0fa4cf82f734665266876b022244543f" +dependencies = [ + "bitflags 1.3.2", + "smallvec", +] + +[[package]] +name = "rustfix" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fb2b066405a6d48a1b39c0022270503e352ae84da0c24e1d5f8ffc38e97a325" +dependencies = [ + "serde", + "serde_json", + "thiserror", + "tracing", +] + +[[package]] +name = "rustix" +version = "0.38.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "375116bee2be9ed569afe2154ea6a99dfdffd257f533f187498c2a8f5feaf4ee" +dependencies = [ + "bitflags 2.6.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustversion" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "semver" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" +dependencies = [ + "serde", +] + +[[package]] +name = "serde" +version = "1.0.214" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.214" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.132" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_spanned" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +dependencies = [ + "serde", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "syn" +version = "2.0.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tempfile" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c" +dependencies = [ + "cfg-if", + "fastrand", + "once_cell", + "rustix", + "windows-sys 0.59.0", +] + +[[package]] +name = "term" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f" +dependencies = [ + "dirs-next", + "rustversion", + "winapi", +] + +[[package]] +name = "tester" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89e8bf7e0eb2dd7b4228cc1b6821fc5114cd6841ae59f652a85488c016091e5f" +dependencies = [ + "cfg-if", + "getopts", + "libc", + "num_cpus", + "term", +] + +[[package]] +name = "thiserror" +version = "1.0.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02dd99dc800bbb97186339685293e1cc5d9df1f8fae2d0aecd9ff1c77efea892" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7c61ec9a6f64d2793d8a45faba21efbe3ced62a886d44c36a009b2b519b4c7e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tinystr" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "toml" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit 0.19.15", +] + +[[package]] +name = "toml" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit 0.22.22", +] + +[[package]] +name = "toml_datetime" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.22.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow 0.6.20", +] + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unicode-ident" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" + +[[package]] +name = "unicode-width" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" + +[[package]] +name = "url" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d157f1b96d14500ffdc1f10ba712e780825526c03d9a49b4d0324b0d9113ada" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[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", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[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", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[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.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[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.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winnow" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + +[[package]] +name = "winnow" +version = "0.6.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" +dependencies = [ + "memchr", +] + +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + +[[package]] +name = "yoke" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c5b1314b079b0930c31e3af543d8ee1757b1951ae1e1565ec704403a7240ca5" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerofrom" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ec111ce797d0e0784a1116d0ddcdbea84322cd79e5d5ad173daeba4f93ab55" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerovec" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/dylints/non_authenticated_routes/Cargo.toml b/dylints/non_authenticated_routes/Cargo.toml new file mode 100644 index 00000000..396c96f3 --- /dev/null +++ b/dylints/non_authenticated_routes/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "non_authenticated_routes" +version = "0.1.0" +authors = ["authors go here"] +description = "description goes here" +edition = "2021" +publish = false + +[lib] +crate-type = ["cdylib"] + +[dependencies] +clippy_utils = { git = "https://github.com/rust-lang/rust-clippy", rev = "4f0e46b74dbc8441daf084b6f141a7fe414672a2" } +dylint_linting = "3.2.1" + +[dev-dependencies] +dylint_testing = "3.2.1" + +[package.metadata.rust-analyzer] +rustc_private = true diff --git a/dylints/non_authenticated_routes/rust-toolchain b/dylints/non_authenticated_routes/rust-toolchain new file mode 100644 index 00000000..3798c445 --- /dev/null +++ b/dylints/non_authenticated_routes/rust-toolchain @@ -0,0 +1,3 @@ +[toolchain] +channel = "nightly-2024-11-09" +components = ["llvm-tools-preview", "rustc-dev"] diff --git a/dylints/non_authenticated_routes/src/lib.rs b/dylints/non_authenticated_routes/src/lib.rs new file mode 100644 index 00000000..78bac586 --- /dev/null +++ b/dylints/non_authenticated_routes/src/lib.rs @@ -0,0 +1,167 @@ +#![feature(rustc_private)] +#![feature(let_chains)] + +extern crate rustc_arena; +extern crate rustc_ast; +extern crate rustc_ast_pretty; +extern crate rustc_attr; +extern crate rustc_data_structures; +extern crate rustc_errors; +extern crate rustc_hir; +extern crate rustc_hir_pretty; +extern crate rustc_index; +extern crate rustc_infer; +extern crate rustc_lexer; +extern crate rustc_middle; +extern crate rustc_mir_dataflow; +extern crate rustc_parse; +extern crate rustc_span; +extern crate rustc_target; +extern crate rustc_trait_selection; + +use clippy_utils::diagnostics::span_lint; +use rustc_hir::{def_id::DefId, Item, ItemKind, QPath, TyKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_span::{symbol::Ident, Span, Symbol}; + +dylint_linting::impl_late_lint! { + /// ### What it does + /// + /// ### Why is this bad? + /// + /// ### Known problems + /// Remove if none. + /// + /// ### Example + /// ```rust + /// // example code where a warning is issued + /// ``` + /// Use instead: + /// ```rust + /// // example code that does not raise a warning + /// ``` + pub NON_AUTHENTICATED_ROUTES, + Warn, + "description goes here", + NonAuthenticatedRoutes::default() +} + +#[derive(Default)] +pub struct NonAuthenticatedRoutes { + last_function_item: Option<(Ident, Span, bool)>, +} + +// Collect all the attribute macros that are applied to the given span +fn attr_def_ids(mut span: rustc_span::Span) -> Vec<(DefId, Symbol, Option)> { + use rustc_span::hygiene::{walk_chain, ExpnKind, MacroKind}; + use rustc_span::{ExpnData, SyntaxContext}; + + let mut def_ids = Vec::new(); + while span.ctxt() != SyntaxContext::root() { + if let ExpnData { + kind: ExpnKind::Macro(MacroKind::Attr, macro_symbol), + macro_def_id: Some(def_id), + parent_module, + .. + } = span.ctxt().outer_expn_data() + { + def_ids.push((def_id, macro_symbol, parent_module)); + } + span = walk_chain(span, SyntaxContext::root()); + } + def_ids +} + +const ROCKET_MACRO_EXCEPTIONS: [(&str, &str); 1] = [("rocket::catch", "catch")]; + +const VALID_AUTH_HEADERS: [&str; 6] = [ + "auth::Headers", + "auth::OrgHeaders", + "auth::AdminHeaders", + "auth::ManagerHeaders", + "auth::ManagerHeadersLoose", + "auth::OwnerHeaders", +]; + +impl<'tcx> LateLintPass<'tcx> for NonAuthenticatedRoutes { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item) { + if let ItemKind::Fn(sig, ..) = item.kind { + let mut has_auth_headers = false; + + for input in sig.decl.inputs { + let TyKind::Path(QPath::Resolved(_, path)) = input.kind else { + continue; + }; + + for seg in path.segments { + if let Some(def_id) = seg.res.opt_def_id() { + let def = cx.tcx.def_path_str(def_id); + if VALID_AUTH_HEADERS.contains(&def.as_str()) { + has_auth_headers = true; + } + } + } + } + + self.last_function_item = Some((item.ident, sig.span, has_auth_headers)); + return; + } + + let ItemKind::Struct(_data, _generics) = item.kind else { + return; + }; + + let def_ids = attr_def_ids(item.span); + + let mut is_rocket_route = false; + + for (def_id, sym, parent) in &def_ids { + let def_id = cx.tcx.def_path_str(*def_id); + let sym = sym.as_str(); + let parent = parent.map(|parent| cx.tcx.def_path_str(parent)); + + if ROCKET_MACRO_EXCEPTIONS.contains(&(&def_id, sym)) { + is_rocket_route = false; + break; + } + + if def_id.starts_with("rocket::") || parent.as_deref() == Some("rocket_codegen") { + is_rocket_route = true; + break; + } + } + + if !is_rocket_route { + return; + } + + let Some((func_ident, func_span, has_auth_headers)) = self.last_function_item.take() else { + span_lint(cx, NON_AUTHENTICATED_ROUTES, item.span, "No function found before the expanded route"); + return; + }; + + if func_ident != item.ident { + span_lint( + cx, + NON_AUTHENTICATED_ROUTES, + item.span, + "The function before the expanded route does not match the route", + ); + return; + } + + if !has_auth_headers { + span_lint( + cx, + NON_AUTHENTICATED_ROUTES, + func_span, + "This Rocket route does not have any authentication headers", + ); + } + } +} + +#[test] +fn ui() { + dylint_testing::ui_test(env!("CARGO_PKG_NAME"), "ui"); +} diff --git a/dylints/non_authenticated_routes/ui/main.rs b/dylints/non_authenticated_routes/ui/main.rs new file mode 100644 index 00000000..f328e4d9 --- /dev/null +++ b/dylints/non_authenticated_routes/ui/main.rs @@ -0,0 +1 @@ +fn main() {} diff --git a/dylints/non_authenticated_routes/ui/main.stderr b/dylints/non_authenticated_routes/ui/main.stderr new file mode 100644 index 00000000..e69de29b From 20d9e885bfcd7df7828d92c6e59ed5fe7b40a879 Mon Sep 17 00:00:00 2001 From: BlackDex Date: Sat, 9 Nov 2024 19:58:10 +0100 Subject: [PATCH 002/286] Update crates and fix several issues Signed-off-by: BlackDex --- Cargo.lock | 187 ++++++++++-------- Cargo.toml | 22 +-- src/api/core/accounts.rs | 170 ++++++++-------- src/api/core/mod.rs | 1 + src/db/models/cipher.rs | 12 +- src/mail.rs | 57 +++++- .../email/send_emergency_access_invite.hbs | 2 +- .../send_emergency_access_invite.html.hbs | 4 +- src/util.rs | 16 +- 9 files changed, 276 insertions(+), 195 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4d59bee1..ae469d23 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -55,9 +55,9 @@ dependencies = [ [[package]] name = "allocator-api2" -version = "0.2.18" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" +checksum = "611cc2ae7d2e242c457e4be7f97036b8ad9ca152b499f53faf99b1ed8fc2553f" [[package]] name = "android-tzdata" @@ -153,9 +153,9 @@ dependencies = [ [[package]] name = "async-io" -version = "2.3.4" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "444b0228950ee6501b3568d3c93bf1176a1fdbc3b758dcd9475046d30f4dc7e8" +checksum = "43a2b323ccce0a1d90b449fd71f2a06ca7faa7c54c2751f06c9bd851fc061059" dependencies = [ "async-lock", "cfg-if", @@ -352,9 +352,9 @@ checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" [[package]] name = "bigdecimal" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d712318a27c7150326677b321a5fa91b55f6d9034ffd67f20319e147d40cee" +checksum = "8f850665a0385e070b64c38d2354e6c104c8479c59868d1e48a0c13ee2c7a1c1" dependencies = [ "autocfg", "libm", @@ -459,9 +459,9 @@ checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da" [[package]] name = "cached" -version = "0.53.1" +version = "0.54.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4d73155ae6b28cf5de4cfc29aeb02b8a1c6dab883cb015d15cd514e42766846" +checksum = "9718806c4a2fe9e8a56fd736f97b340dd10ed1be8ed733ed50449f351dc33cae" dependencies = [ "ahash", "async-trait", @@ -495,9 +495,9 @@ checksum = "ade8366b8bd5ba243f0a58f036cc0ca8a2f069cff1a2351ef1cac6b083e16fc0" [[package]] name = "cc" -version = "1.1.31" +version = "1.1.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2e7962b54006dcfcc61cb72735f4d89bb97061dd6a7ed882ec6b8ee53714c6f" +checksum = "40545c26d092346d8a8dab71ee48e7685a7a9cba76e634790c215b41a4a7b4cf" dependencies = [ "shlex", ] @@ -574,12 +574,13 @@ dependencies = [ [[package]] name = "cookie_store" -version = "0.21.0" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4934e6b7e8419148b6ef56950d277af8561060b56afd59e2aadf98b59fce6baa" +checksum = "2eac901828f88a5241ee0600950ab981148a18f2f756900ffba1b125ca6a3ef9" dependencies = [ "cookie", - "idna 0.5.0", + "document-features", + "idna 1.0.3", "log", "publicsuffix", "serde", @@ -842,6 +843,15 @@ dependencies = [ "syn", ] +[[package]] +name = "document-features" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb6969eaabd2421f8a2775cfd2471a2b634372b4a25d41e3bd647b79912850a0" +dependencies = [ + "litrs", +] + [[package]] name = "dotenvy" version = "0.15.7" @@ -962,9 +972,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" +checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4" [[package]] name = "fern" @@ -1082,9 +1092,9 @@ checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-lite" -version = "2.3.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" +checksum = "cef40d21ae2c515b51041df9ed313ed21e572df340ea58a922a0aefe7e8891a1" dependencies = [ "fastrand", "futures-core", @@ -1267,11 +1277,12 @@ checksum = "1b43ede17f21864e81be2fa654110bf1e793774238d86ef8555c37e6519c0403" [[package]] name = "handlebars" -version = "6.1.0" +version = "6.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce25b617d1375ef96eeb920ae717e3da34a02fc979fe632c75128350f9e1f74a" +checksum = "fd4ccde012831f9a071a637b0d4e31df31c0f6c525784b35ae76a9ac6bc1e315" dependencies = [ "log", + "num-order", "pest", "pest_derive", "serde", @@ -1292,9 +1303,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.0" +version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" +checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3" [[package]] name = "heck" @@ -1401,9 +1412,9 @@ dependencies = [ [[package]] name = "html5gum" -version = "0.5.7" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c4e556171a058ba117bbe88b059fb37b6289023e007d2903ea6dca3a3cbff14" +checksum = "91b361633dcc40096d01de35ed535b6089be91880be47b6fd8f560497af7f716" dependencies = [ "jetscii", ] @@ -1530,7 +1541,7 @@ dependencies = [ "http 1.1.0", "hyper 1.5.0", "hyper-util", - "rustls 0.23.15", + "rustls 0.23.16", "rustls-pki-types", "tokio", "tokio-rustls 0.26.0", @@ -1568,9 +1579,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41296eb09f183ac68eec06e03cdbea2e759633d4067b2f6552fc2e009bcad08b" +checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" dependencies = [ "bytes", "futures-channel", @@ -1754,24 +1765,23 @@ dependencies = [ [[package]] name = "idna" -version = "0.5.0" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" dependencies = [ - "unicode-bidi", - "unicode-normalization", + "idna_adapter", + "smallvec", + "utf8_iter", ] [[package]] -name = "idna" -version = "1.0.2" +name = "idna_adapter" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd69211b9b519e98303c015e21a007e293db403b6c85b9b124e133d25e242cdd" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" dependencies = [ "icu_normalizer", "icu_properties", - "smallvec", - "utf8_iter", ] [[package]] @@ -1781,7 +1791,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" dependencies = [ "equivalent", - "hashbrown 0.15.0", + "hashbrown 0.15.1", "serde", ] @@ -1899,7 +1909,7 @@ dependencies = [ "futures-util", "hostname 0.4.0", "httpdate", - "idna 1.0.2", + "idna 1.0.3", "mime", "native-tls", "nom", @@ -1915,15 +1925,15 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.161" +version = "0.2.162" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" +checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398" [[package]] name = "libm" -version = "0.2.8" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" +checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" [[package]] name = "libmimalloc-sys" @@ -1964,6 +1974,12 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "643cb0b8d4fcc284004d5fd0d67ccf61dfffadb7f75e1e71bc420f4688a3a704" +[[package]] +name = "litrs" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5" + [[package]] name = "lock_api" version = "0.4.12" @@ -2205,6 +2221,21 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-modular" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17bb261bf36fa7d83f4c294f834e91256769097b3cb505d44831e0a179ac647f" + +[[package]] +name = "num-order" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "537b596b97c40fcf8056d153049eb22f481c17ebce72a513ec9286e4986d1bb6" +dependencies = [ + "num-modular", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -2493,9 +2524,9 @@ checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" [[package]] name = "pin-project-lite" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" +checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" [[package]] name = "pin-utils" @@ -2522,9 +2553,9 @@ checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" [[package]] name = "polling" -version = "3.7.3" +version = "3.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc2790cd301dec6cd3b7a025e4815cf825724a51c98dccfe6a3e55f05ffb6511" +checksum = "a604568c3202727d1507653cb121dbd627a58684eb09a820fd746bee38b4442f" dependencies = [ "cfg-if", "concurrent-queue", @@ -2729,9 +2760,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.11.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", @@ -2824,9 +2855,9 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.12.8" +version = "0.12.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f713147fbe92361e52392c73b8c9e48c04c6625bce969ef54dc901e58e042a7b" +checksum = "a77c62af46e79de0a562e1a9849205ffcb7fc1238876e9bd743357570e04046f" dependencies = [ "async-compression", "base64 0.22.1", @@ -3041,9 +3072,9 @@ checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustix" -version = "0.38.37" +version = "0.38.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" +checksum = "99e4ea3e1cdc4b559b8e5650f9c8e5998e3e5c1343b4eaf034565f32318d63c0" dependencies = [ "bitflags 2.6.0", "errno", @@ -3066,9 +3097,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.15" +version = "0.23.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fbb44d7acc4e873d613422379f69f237a1b141928c02f6bc6ccfddddc2d7993" +checksum = "eee87ff5d9b36712a58574e12e9f0ea80f915a5b0ac518d322b24a465617925e" dependencies = [ "once_cell", "rustls-pki-types", @@ -3198,9 +3229,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.12.0" +version = "2.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea4a292869320c0272d7bc55a5a6aafaff59b4f63404a003887b679a2e05b4b6" +checksum = "fa39c7303dc58b5543c94d22c1766b0d31f2ee58306363ea622b10bbc075eaa2" dependencies = [ "core-foundation-sys", "libc", @@ -3214,9 +3245,9 @@ checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" [[package]] name = "serde" -version = "1.0.213" +version = "1.0.214" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ea7893ff5e2466df8d720bb615088341b295f849602c6956047f8f80f0e9bc1" +checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5" dependencies = [ "serde_derive", ] @@ -3233,9 +3264,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.213" +version = "1.0.214" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e85ad2009c50b58e87caa8cd6dac16bdf511bbfb7af6c33df902396aa480fa5" +checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766" dependencies = [ "proc-macro2", "quote", @@ -3440,9 +3471,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.85" +version = "2.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5023162dfcd14ef8f32034d8bcd4cc5ddc61ef7a247c024a33e24e1f24d21b56" +checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" dependencies = [ "proc-macro2", "quote", @@ -3532,9 +3563,9 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.13.0" +version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b" +checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c" dependencies = [ "cfg-if", "fastrand", @@ -3545,18 +3576,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.65" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d11abd9594d9b38965ef50805c5e469ca9cc6f197f883f717e0269a3057b3d5" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.65" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae71770322cbd277e69d762a16c444af02aa0575ac0d174f0b9562d3b37f8602" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", @@ -3642,9 +3673,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.41.0" +version = "1.41.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "145f3413504347a2be84393cc8a7d2fb4d863b375909ea59f2158261aa258bbb" +checksum = "22cfb5bee7a6a52939ca9224d6ac897bb669134078daa8735560897f69de4d33" dependencies = [ "backtrace", "bytes", @@ -3695,7 +3726,7 @@ version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" dependencies = [ - "rustls 0.23.15", + "rustls 0.23.16", "rustls-pki-types", "tokio", ] @@ -3953,12 +3984,12 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.5.2" +version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +checksum = "8d157f1b96d14500ffdc1f10ba712e780825526c03d9a49b4d0324b0d9113ada" dependencies = [ "form_urlencoded", - "idna 0.5.0", + "idna 1.0.3", "percent-encoding", "serde", ] @@ -4043,7 +4074,7 @@ dependencies = [ "pico-args", "rand", "regex", - "reqwest 0.12.8", + "reqwest 0.12.9", "ring", "rmpv", "rocket", @@ -4170,9 +4201,9 @@ checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" [[package]] name = "wasm-streams" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e072d4e72f700fb3443d8fe94a39315df013eef1104903cdb0a2abd322bbecd" +checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" dependencies = [ "futures-util", "js-sys", @@ -4222,9 +4253,9 @@ dependencies = [ [[package]] name = "which" -version = "6.0.3" +version = "7.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4ee928febd44d98f2f459a4a79bd4d928591333a494a10a868418ac1b39cf1f" +checksum = "c9cad3279ade7346b96e38731a641d7343dd6a53d55083dd54eadfa5a1b38c6b" dependencies = [ "either", "home", diff --git a/Cargo.toml b/Cargo.toml index 934e0321..78a1a4b5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -53,7 +53,7 @@ once_cell = "1.20.2" # Numerical libraries num-traits = "0.2.19" num-derive = "0.4.2" -bigdecimal = "0.4.5" +bigdecimal = "0.4.6" # Web framework rocket = { version = "0.5.1", features = ["tls", "json"], default-features = false } @@ -67,10 +67,10 @@ dashmap = "6.1.0" # Async futures futures = "0.3.31" -tokio = { version = "1.41.0", features = ["rt-multi-thread", "fs", "io-util", "parking_lot", "time", "signal", "net"] } +tokio = { version = "1.41.1", features = ["rt-multi-thread", "fs", "io-util", "parking_lot", "time", "signal", "net"] } # A generic serialization/deserialization framework -serde = { version = "1.0.213", features = ["derive"] } +serde = { version = "1.0.214", features = ["derive"] } serde_json = "1.0.132" # A safe, extensible ORM and Query builder @@ -112,7 +112,7 @@ yubico = { version = "0.11.0", features = ["online-tokio"], default-features = f webauthn-rs = "0.3.2" # Handling of URL's for WebAuthn and favicons -url = "2.5.2" +url = "2.5.3" # Email libraries lettre = { version = "0.11.10", features = ["smtp-transport", "sendmail-transport", "builder", "serde", "tokio1-native-tls", "hostname", "tracing", "tokio1"], default-features = false } @@ -120,24 +120,24 @@ percent-encoding = "2.3.1" # URL encoding library used for URL's in the emails email_address = "0.2.9" # HTML Template library -handlebars = { version = "6.1.0", features = ["dir_source"] } +handlebars = { version = "6.2.0", features = ["dir_source"] } # HTTP client (Used for favicons, version check, DUO and HIBP API) -reqwest = { version = "0.12.8", features = ["native-tls-alpn", "stream", "json", "gzip", "brotli", "socks", "cookies"] } +reqwest = { version = "0.12.9", features = ["native-tls-alpn", "stream", "json", "gzip", "brotli", "socks", "cookies"] } hickory-resolver = "0.24.1" # Favicon extraction libraries -html5gum = "0.5.7" -regex = { version = "1.11.0", features = ["std", "perf", "unicode-perl"], default-features = false } +html5gum = "0.6.1" +regex = { version = "1.11.1", features = ["std", "perf", "unicode-perl"], default-features = false } data-url = "0.3.1" bytes = "1.8.0" # Cache function results (Used for version check and favicon fetching) -cached = { version = "0.53.1", features = ["async"] } +cached = { version = "0.54.0", features = ["async"] } # Used for custom short lived cookie jar during favicon extraction cookie = "0.18.1" -cookie_store = "0.21.0" +cookie_store = "0.21.1" # Used by U2F, JWT and PostgreSQL openssl = "0.10.68" @@ -155,7 +155,7 @@ semver = "1.0.23" # Allow overriding the default memory allocator # Mainly used for the musl builds, since the default musl malloc is very slow mimalloc = { version = "0.1.43", features = ["secure"], default-features = false, optional = true } -which = "6.0.3" +which = "7.0.0" # Argon2 library with support for the PHC format argon2 = "0.5.3" diff --git a/src/api/core/accounts.rs b/src/api/core/accounts.rs index f9822629..71609b37 100644 --- a/src/api/core/accounts.rs +++ b/src/api/core/accounts.rs @@ -1,5 +1,5 @@ use crate::db::DbPool; -use chrono::{SecondsFormat, Utc}; +use chrono::Utc; use rocket::serde::json::Json; use serde_json::Value; @@ -13,7 +13,7 @@ use crate::{ crypto, db::{models::*, DbConn}, mail, - util::NumberOrString, + util::{format_date, NumberOrString}, CONFIG, }; @@ -901,14 +901,12 @@ pub async fn _prelogin(data: Json, mut conn: DbConn) -> Json (User::CLIENT_KDF_TYPE_DEFAULT, User::CLIENT_KDF_ITER_DEFAULT, None, None), }; - let result = json!({ + Json(json!({ "kdf": kdf_type, "kdfIterations": kdf_iter, "kdfMemory": kdf_mem, "kdfParallelism": kdf_para, - }); - - Json(result) + })) } // https://github.com/bitwarden/server/blob/master/src/Api/Models/Request/Accounts/SecretVerificationRequestModel.cs @@ -1084,14 +1082,15 @@ struct AuthRequestRequest { device_identifier: String, email: String, public_key: String, - #[serde(alias = "type")] - _type: i32, + // Not used for now + // #[serde(alias = "type")] + // _type: i32, } #[post("/auth-requests", data = "")] async fn post_auth_request( data: Json, - headers: ClientHeaders, + client_headers: ClientHeaders, mut conn: DbConn, nt: Notify<'_>, ) -> JsonResult { @@ -1099,16 +1098,20 @@ async fn post_auth_request( let user = match User::find_by_mail(&data.email, &mut conn).await { Some(user) => user, - None => { - err!("AuthRequest doesn't exist") - } + None => err!("AuthRequest doesn't exist", "User not found"), }; + // Validate device uuid and type + match Device::find_by_uuid_and_user(&data.device_identifier, &user.uuid, &mut conn).await { + Some(device) if device.atype == client_headers.device_type => {} + _ => err!("AuthRequest doesn't exist", "Device verification failed"), + } + let mut auth_request = AuthRequest::new( user.uuid.clone(), data.device_identifier.clone(), - headers.device_type, - headers.ip.ip.to_string(), + client_headers.device_type, + client_headers.ip.ip.to_string(), data.access_code, data.public_key, ); @@ -1123,7 +1126,7 @@ async fn post_auth_request( "requestIpAddress": auth_request.request_ip, "key": null, "masterPasswordHash": null, - "creationDate": auth_request.creation_date.and_utc().to_rfc3339_opts(SecondsFormat::Micros, true), + "creationDate": format_date(&auth_request.creation_date), "responseDate": null, "requestApproved": false, "origin": CONFIG.domain_origin(), @@ -1132,33 +1135,31 @@ async fn post_auth_request( } #[get("/auth-requests/")] -async fn get_auth_request(uuid: &str, mut conn: DbConn) -> JsonResult { +async fn get_auth_request(uuid: &str, headers: Headers, mut conn: DbConn) -> JsonResult { + if headers.user.uuid != uuid { + err!("AuthRequest doesn't exist", "User uuid's do not match") + } + let auth_request = match AuthRequest::find_by_uuid(uuid, &mut conn).await { Some(auth_request) => auth_request, - None => { - err!("AuthRequest doesn't exist") - } + None => err!("AuthRequest doesn't exist", "Record not found"), }; - let response_date_utc = auth_request - .response_date - .map(|response_date| response_date.and_utc().to_rfc3339_opts(SecondsFormat::Micros, true)); + let response_date_utc = auth_request.response_date.map(|response_date| format_date(&response_date)); - Ok(Json(json!( - { - "id": uuid, - "publicKey": auth_request.public_key, - "requestDeviceType": DeviceType::from_i32(auth_request.device_type).to_string(), - "requestIpAddress": auth_request.request_ip, - "key": auth_request.enc_key, - "masterPasswordHash": auth_request.master_password_hash, - "creationDate": auth_request.creation_date.and_utc().to_rfc3339_opts(SecondsFormat::Micros, true), - "responseDate": response_date_utc, - "requestApproved": auth_request.approved, - "origin": CONFIG.domain_origin(), - "object":"auth-request" - } - ))) + Ok(Json(json!({ + "id": uuid, + "publicKey": auth_request.public_key, + "requestDeviceType": DeviceType::from_i32(auth_request.device_type).to_string(), + "requestIpAddress": auth_request.request_ip, + "key": auth_request.enc_key, + "masterPasswordHash": auth_request.master_password_hash, + "creationDate": format_date(&auth_request.creation_date), + "responseDate": response_date_utc, + "requestApproved": auth_request.approved, + "origin": CONFIG.domain_origin(), + "object":"auth-request" + }))) } #[derive(Debug, Deserialize)] @@ -1174,6 +1175,7 @@ struct AuthResponseRequest { async fn put_auth_request( uuid: &str, data: Json, + headers: Headers, mut conn: DbConn, ant: AnonymousNotify<'_>, nt: Notify<'_>, @@ -1181,11 +1183,13 @@ async fn put_auth_request( let data = data.into_inner(); let mut auth_request: AuthRequest = match AuthRequest::find_by_uuid(uuid, &mut conn).await { Some(auth_request) => auth_request, - None => { - err!("AuthRequest doesn't exist") - } + None => err!("AuthRequest doesn't exist", "Record not found"), }; + if headers.user.uuid != auth_request.user_uuid { + err!("AuthRequest doesn't exist", "User uuid's do not match") + } + auth_request.approved = Some(data.request_approved); auth_request.enc_key = Some(data.key); auth_request.master_password_hash = data.master_password_hash; @@ -1197,59 +1201,57 @@ async fn put_auth_request( nt.send_auth_response(&auth_request.user_uuid, &auth_request.uuid, data.device_identifier, &mut conn).await; } - let response_date_utc = auth_request - .response_date - .map(|response_date| response_date.and_utc().to_rfc3339_opts(SecondsFormat::Micros, true)); + let response_date_utc = auth_request.response_date.map(|response_date| format_date(&response_date)); - Ok(Json(json!( - { - "id": uuid, - "publicKey": auth_request.public_key, - "requestDeviceType": DeviceType::from_i32(auth_request.device_type).to_string(), - "requestIpAddress": auth_request.request_ip, - "key": auth_request.enc_key, - "masterPasswordHash": auth_request.master_password_hash, - "creationDate": auth_request.creation_date.and_utc().to_rfc3339_opts(SecondsFormat::Micros, true), - "responseDate": response_date_utc, - "requestApproved": auth_request.approved, - "origin": CONFIG.domain_origin(), - "object":"auth-request" - } - ))) + Ok(Json(json!({ + "id": uuid, + "publicKey": auth_request.public_key, + "requestDeviceType": DeviceType::from_i32(auth_request.device_type).to_string(), + "requestIpAddress": auth_request.request_ip, + "key": auth_request.enc_key, + "masterPasswordHash": auth_request.master_password_hash, + "creationDate": format_date(&auth_request.creation_date), + "responseDate": response_date_utc, + "requestApproved": auth_request.approved, + "origin": CONFIG.domain_origin(), + "object":"auth-request" + }))) } #[get("/auth-requests//response?")] -async fn get_auth_request_response(uuid: &str, code: &str, mut conn: DbConn) -> JsonResult { +async fn get_auth_request_response( + uuid: &str, + code: &str, + client_headers: ClientHeaders, + mut conn: DbConn, +) -> JsonResult { let auth_request = match AuthRequest::find_by_uuid(uuid, &mut conn).await { Some(auth_request) => auth_request, - None => { - err!("AuthRequest doesn't exist") - } + None => err!("AuthRequest doesn't exist", "User not found"), }; - if !auth_request.check_access_code(code) { - err!("Access code invalid doesn't exist") + if auth_request.device_type != client_headers.device_type + && auth_request.request_ip != client_headers.ip.ip.to_string() + && !auth_request.check_access_code(code) + { + err!("AuthRequest doesn't exist", "Invalid device, IP or code") } - let response_date_utc = auth_request - .response_date - .map(|response_date| response_date.and_utc().to_rfc3339_opts(SecondsFormat::Micros, true)); + let response_date_utc = auth_request.response_date.map(|response_date| format_date(&response_date)); - Ok(Json(json!( - { - "id": uuid, - "publicKey": auth_request.public_key, - "requestDeviceType": DeviceType::from_i32(auth_request.device_type).to_string(), - "requestIpAddress": auth_request.request_ip, - "key": auth_request.enc_key, - "masterPasswordHash": auth_request.master_password_hash, - "creationDate": auth_request.creation_date.and_utc().to_rfc3339_opts(SecondsFormat::Micros, true), - "responseDate": response_date_utc, - "requestApproved": auth_request.approved, - "origin": CONFIG.domain_origin(), - "object":"auth-request" - } - ))) + Ok(Json(json!({ + "id": uuid, + "publicKey": auth_request.public_key, + "requestDeviceType": DeviceType::from_i32(auth_request.device_type).to_string(), + "requestIpAddress": auth_request.request_ip, + "key": auth_request.enc_key, + "masterPasswordHash": auth_request.master_password_hash, + "creationDate": format_date(&auth_request.creation_date), + "responseDate": response_date_utc, + "requestApproved": auth_request.approved, + "origin": CONFIG.domain_origin(), + "object":"auth-request" + }))) } #[get("/auth-requests")] @@ -1261,7 +1263,7 @@ async fn get_auth_requests(headers: Headers, mut conn: DbConn) -> JsonResult { .iter() .filter(|request| request.approved.is_none()) .map(|request| { - let response_date_utc = request.response_date.map(|response_date| response_date.and_utc().to_rfc3339_opts(SecondsFormat::Micros, true)); + let response_date_utc = request.response_date.map(|response_date| format_date(&response_date)); json!({ "id": request.uuid, @@ -1270,7 +1272,7 @@ async fn get_auth_requests(headers: Headers, mut conn: DbConn) -> JsonResult { "requestIpAddress": request.request_ip, "key": request.enc_key, "masterPasswordHash": request.master_password_hash, - "creationDate": request.creation_date.and_utc().to_rfc3339_opts(SecondsFormat::Micros, true), + "creationDate": format_date(&request.creation_date), "responseDate": response_date_utc, "requestApproved": request.approved, "origin": CONFIG.domain_origin(), diff --git a/src/api/core/mod.rs b/src/api/core/mod.rs index ee5db190..4ac6b777 100644 --- a/src/api/core/mod.rs +++ b/src/api/core/mod.rs @@ -136,6 +136,7 @@ async fn put_eq_domains(data: Json, headers: Headers, conn: DbC #[get("/hibp/breach?")] async fn hibp_breach(username: &str) -> JsonResult { + let username: String = url::form_urlencoded::byte_serialize(username.as_bytes()).collect(); let url = format!( "https://haveibeenpwned.com/api/v3/breachedaccount/{username}?truncateResponse=false&includeUnverified=false" ); diff --git a/src/db/models/cipher.rs b/src/db/models/cipher.rs index fb2b5021..9c568284 100644 --- a/src/db/models/cipher.rs +++ b/src/db/models/cipher.rs @@ -1,6 +1,6 @@ use crate::util::LowerCase; use crate::CONFIG; -use chrono::{DateTime, NaiveDateTime, TimeDelta, Utc}; +use chrono::{NaiveDateTime, TimeDelta, Utc}; use serde_json::Value; use super::{ @@ -216,11 +216,13 @@ impl Cipher { Some(p) if p.is_string() => Some(d.data), _ => None, }) - .map(|d| match d.get("lastUsedDate").and_then(|l| l.as_str()) { - Some(l) if DateTime::parse_from_rfc3339(l).is_ok() => d, + .map(|mut d| match d.get("lastUsedDate").and_then(|l| l.as_str()) { + Some(l) => { + d["lastUsedDate"] = json!(crate::util::validate_and_format_date(l)); + d + } _ => { - let mut d = d; - d["lastUsedDate"] = json!("1970-01-01T00:00:00.000Z"); + d["lastUsedDate"] = json!("1970-01-01T00:00:00.000000Z"); d } }) diff --git a/src/mail.rs b/src/mail.rs index b33efd95..5ce4a079 100644 --- a/src/mail.rs +++ b/src/mail.rs @@ -96,7 +96,31 @@ fn smtp_transport() -> AsyncSmtpTransport { smtp_client.build() } +// This will sanitize the string values by stripping all the html tags to prevent XSS and HTML Injections +fn sanitize_data(data: &mut serde_json::Value) { + use regex::Regex; + use std::sync::LazyLock; + static RE: LazyLock = LazyLock::new(|| Regex::new(r"<[^>]+>").unwrap()); + + match data { + serde_json::Value::String(s) => *s = RE.replace_all(s, "").to_string(), + serde_json::Value::Object(obj) => { + for d in obj.values_mut() { + sanitize_data(d); + } + } + serde_json::Value::Array(arr) => { + for d in arr.iter_mut() { + sanitize_data(d); + } + } + _ => {} + } +} + fn get_text(template_name: &'static str, data: serde_json::Value) -> Result<(String, String, String), Error> { + let mut data = data; + sanitize_data(&mut data); let (subject_html, body_html) = get_template(&format!("{template_name}.html"), &data)?; let (_subject_text, body_text) = get_template(template_name, &data)?; Ok((subject_html, body_html, body_text)) @@ -116,6 +140,10 @@ fn get_template(template_name: &str, data: &serde_json::Value) -> Result<(String None => err!("Template doesn't contain body"), }; + if text_split.next().is_some() { + err!("Template contains more than one body"); + } + Ok((subject, body)) } @@ -259,16 +287,15 @@ pub async fn send_invite( } let query_string = match query.query() { - None => err!(format!("Failed to build invite URL query parameters")), + None => err!("Failed to build invite URL query parameters"), Some(query) => query, }; - // `url.Url` would place the anchor `#` after the query parameters - let url = format!("{}/#/accept-organization/?{}", CONFIG.domain(), query_string); let (subject, body_html, body_text) = get_text( "email/send_org_invite", json!({ - "url": url, + // `url.Url` would place the anchor `#` after the query parameters + "url": format!("{}/#/accept-organization/?{}", CONFIG.domain(), query_string), "img_src": CONFIG._smtp_img_src(), "org_name": org_name, }), @@ -292,17 +319,29 @@ pub async fn send_emergency_access_invite( String::from(grantor_email), ); - let invite_token = encode_jwt(&claims); + // Build the query here to ensure proper escaping + let mut query = url::Url::parse("https://query.builder").unwrap(); + { + let mut query_params = query.query_pairs_mut(); + query_params + .append_pair("id", emer_id) + .append_pair("name", grantor_name) + .append_pair("email", address) + .append_pair("token", &encode_jwt(&claims)); + } + + let query_string = match query.query() { + None => err!("Failed to build emergency invite URL query parameters"), + Some(query) => query, + }; let (subject, body_html, body_text) = get_text( "email/send_emergency_access_invite", json!({ - "url": CONFIG.domain(), + // `url.Url` would place the anchor `#` after the query parameters + "url": format!("{}/#/accept-emergency/?{query_string}", CONFIG.domain()), "img_src": CONFIG._smtp_img_src(), - "emer_id": emer_id, - "email": percent_encode(address.as_bytes(), NON_ALPHANUMERIC).to_string(), "grantor_name": grantor_name, - "token": invite_token, }), )?; diff --git a/src/static/templates/email/send_emergency_access_invite.hbs b/src/static/templates/email/send_emergency_access_invite.hbs index 9dc114c0..f4817628 100644 --- a/src/static/templates/email/send_emergency_access_invite.hbs +++ b/src/static/templates/email/send_emergency_access_invite.hbs @@ -2,7 +2,7 @@ Emergency access for {{{grantor_name}}} You have been invited to become an emergency contact for {{grantor_name}}. To accept this invite, click the following link: -Click here to join: {{url}}/#/accept-emergency/?id={{emer_id}}&name={{grantor_name}}&email={{email}}&token={{token}} +Click here to join: {{{url}}} If you do not wish to become an emergency contact for {{grantor_name}}, you can safely ignore this email. {{> email/email_footer_text }} diff --git a/src/static/templates/email/send_emergency_access_invite.html.hbs b/src/static/templates/email/send_emergency_access_invite.html.hbs index fd1c0400..5318378c 100644 --- a/src/static/templates/email/send_emergency_access_invite.html.hbs +++ b/src/static/templates/email/send_emergency_access_invite.html.hbs @@ -9,7 +9,7 @@ Emergency access for {{{grantor_name}}} - Become emergency contact @@ -21,4 +21,4 @@ Emergency access for {{{grantor_name}}} -{{> email/email_footer }} \ No newline at end of file +{{> email/email_footer }} diff --git a/src/util.rs b/src/util.rs index d8433b9a..1f3ba9cf 100644 --- a/src/util.rs +++ b/src/util.rs @@ -438,13 +438,19 @@ pub fn get_env_bool(key: &str) -> Option { use chrono::{DateTime, Local, NaiveDateTime, TimeZone}; -// Format used by Bitwarden API -const DATETIME_FORMAT: &str = "%Y-%m-%dT%H:%M:%S%.6fZ"; - /// Formats a UTC-offset `NaiveDateTime` in the format used by Bitwarden API /// responses with "date" fields (`CreationDate`, `RevisionDate`, etc.). pub fn format_date(dt: &NaiveDateTime) -> String { - dt.format(DATETIME_FORMAT).to_string() + dt.and_utc().to_rfc3339_opts(chrono::SecondsFormat::Micros, true) +} + +/// Validates and formats a RFC3339 timestamp +/// If parsing fails it will return the start of the unix datetime +pub fn validate_and_format_date(dt: &str) -> String { + match DateTime::parse_from_rfc3339(dt) { + Ok(dt) => dt.to_rfc3339_opts(chrono::SecondsFormat::Micros, true), + _ => String::from("1970-01-01T00:00:00.000000Z"), + } } /// Formats a `DateTime` using the specified format string. @@ -486,7 +492,7 @@ pub fn format_datetime_http(dt: &DateTime) -> String { } pub fn parse_date(date: &str) -> NaiveDateTime { - NaiveDateTime::parse_from_str(date, DATETIME_FORMAT).unwrap() + DateTime::parse_from_rfc3339(date).unwrap().naive_utc() } // From 38aad4f7bedfb4279ecb385e036b1d84f3d59483 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garc=C3=ADa?= Date: Sun, 10 Nov 2024 23:59:06 +0100 Subject: [PATCH 003/286] Limit HIBP to authed users --- src/api/core/mod.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/api/core/mod.rs b/src/api/core/mod.rs index 4ac6b777..1638afe5 100644 --- a/src/api/core/mod.rs +++ b/src/api/core/mod.rs @@ -135,13 +135,13 @@ async fn put_eq_domains(data: Json, headers: Headers, conn: DbC } #[get("/hibp/breach?")] -async fn hibp_breach(username: &str) -> JsonResult { - let username: String = url::form_urlencoded::byte_serialize(username.as_bytes()).collect(); - let url = format!( - "https://haveibeenpwned.com/api/v3/breachedaccount/{username}?truncateResponse=false&includeUnverified=false" - ); - +async fn hibp_breach(username: &str, _headers: Headers) -> JsonResult { if let Some(api_key) = crate::CONFIG.hibp_api_key() { + let username: String = url::form_urlencoded::byte_serialize(username.as_bytes()).collect(); + let url = format!( + "https://haveibeenpwned.com/api/v3/breachedaccount/{username}?truncateResponse=false&includeUnverified=false" + ); + let res = make_http_request(Method::GET, &url)?.header("hibp-api-key", api_key).send().await?; // If we get a 404, return a 404, it means no breached accounts From d0581da63858885937205ab7e2d1233d4b56c623 Mon Sep 17 00:00:00 2001 From: Mathijs van Veluw Date: Mon, 11 Nov 2024 11:50:33 +0100 Subject: [PATCH 004/286] Fix if logic error (#5171) Fixing a logical error in an if statement where we used `&&` which should have been `||`. Signed-off-by: BlackDex --- src/api/core/accounts.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/api/core/accounts.rs b/src/api/core/accounts.rs index 71609b37..e715d8bd 100644 --- a/src/api/core/accounts.rs +++ b/src/api/core/accounts.rs @@ -1231,8 +1231,8 @@ async fn get_auth_request_response( }; if auth_request.device_type != client_headers.device_type - && auth_request.request_ip != client_headers.ip.ip.to_string() - && !auth_request.check_access_code(code) + || auth_request.request_ip != client_headers.ip.ip.to_string() + || !auth_request.check_access_code(code) { err!("AuthRequest doesn't exist", "Invalid device, IP or code") } From 37c14c3c69b244ec50f5c62b4c9260171607c1d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garc=C3=ADa?= Date: Mon, 11 Nov 2024 20:13:02 +0100 Subject: [PATCH 005/286] More authrequest fixes (#5176) --- src/api/core/accounts.rs | 23 ++++++++------- src/api/identity.rs | 60 +++++++++++++++++++++++----------------- 2 files changed, 47 insertions(+), 36 deletions(-) diff --git a/src/api/core/accounts.rs b/src/api/core/accounts.rs index e715d8bd..e6654add 100644 --- a/src/api/core/accounts.rs +++ b/src/api/core/accounts.rs @@ -1136,15 +1136,15 @@ async fn post_auth_request( #[get("/auth-requests/")] async fn get_auth_request(uuid: &str, headers: Headers, mut conn: DbConn) -> JsonResult { - if headers.user.uuid != uuid { - err!("AuthRequest doesn't exist", "User uuid's do not match") - } - let auth_request = match AuthRequest::find_by_uuid(uuid, &mut conn).await { Some(auth_request) => auth_request, None => err!("AuthRequest doesn't exist", "Record not found"), }; + if headers.user.uuid != auth_request.user_uuid { + err!("AuthRequest doesn't exist", "User uuid's do not match") + } + let response_date_utc = auth_request.response_date.map(|response_date| format_date(&response_date)); Ok(Json(json!({ @@ -1190,15 +1190,18 @@ async fn put_auth_request( err!("AuthRequest doesn't exist", "User uuid's do not match") } - auth_request.approved = Some(data.request_approved); - auth_request.enc_key = Some(data.key); - auth_request.master_password_hash = data.master_password_hash; - auth_request.response_device_id = Some(data.device_identifier.clone()); - auth_request.save(&mut conn).await?; + if data.request_approved { + auth_request.approved = Some(data.request_approved); + auth_request.enc_key = Some(data.key); + auth_request.master_password_hash = data.master_password_hash; + auth_request.response_device_id = Some(data.device_identifier.clone()); + auth_request.save(&mut conn).await?; - if auth_request.approved.unwrap_or(false) { ant.send_auth_response(&auth_request.user_uuid, &auth_request.uuid).await; nt.send_auth_response(&auth_request.user_uuid, &auth_request.uuid, data.device_identifier, &mut conn).await; + } else { + // If denied, there's no reason to keep the request + auth_request.delete(&mut conn).await?; } let response_date_utc = auth_request.response_date.map(|response_date| format_date(&response_date)); diff --git a/src/api/identity.rs b/src/api/identity.rs index 672f128c..003e4d97 100644 --- a/src/api/identity.rs +++ b/src/api/identity.rs @@ -165,20 +165,22 @@ async fn _password_login( // Set the user_uuid here to be passed back used for event logging. *user_uuid = Some(user.uuid.clone()); - // Check password - let password = data.password.as_ref().unwrap(); - if let Some(auth_request_uuid) = data.auth_request.clone() { - if let Some(auth_request) = AuthRequest::find_by_uuid(auth_request_uuid.as_str(), conn).await { - if !auth_request.check_access_code(password) { - err!( - "Username or access code is incorrect. Try again", - format!("IP: {}. Username: {}.", ip.ip, username), - ErrorEvent { - event: EventType::UserFailedLogIn, - } - ) + // Check if the user is disabled + if !user.enabled { + err!( + "This user has been disabled", + format!("IP: {}. Username: {}.", ip.ip, username), + ErrorEvent { + event: EventType::UserFailedLogIn } - } else { + ) + } + + let password = data.password.as_ref().unwrap(); + + // If we get an auth request, we don't check the user's password, but the access code of the auth request + if let Some(ref auth_request_uuid) = data.auth_request { + let Some(auth_request) = AuthRequest::find_by_uuid(auth_request_uuid.as_str(), conn).await else { err!( "Auth request not found. Try again.", format!("IP: {}. Username: {}.", ip.ip, username), @@ -186,6 +188,23 @@ async fn _password_login( event: EventType::UserFailedLogIn, } ) + }; + + // Delete the request after we used it + auth_request.delete(conn).await?; + + if auth_request.user_uuid != user.uuid + || !auth_request.approved.unwrap_or(false) + || ip.ip.to_string() != auth_request.request_ip + || !auth_request.check_access_code(password) + { + err!( + "Username or access code is incorrect. Try again", + format!("IP: {}. Username: {}.", ip.ip, username), + ErrorEvent { + event: EventType::UserFailedLogIn, + } + ) } } else if !user.check_valid_password(password) { err!( @@ -197,8 +216,8 @@ async fn _password_login( ) } - // Change the KDF Iterations - if user.password_iterations != CONFIG.password_iterations() { + // Change the KDF Iterations (only when not logging in with an auth request) + if data.auth_request.is_none() && user.password_iterations != CONFIG.password_iterations() { user.password_iterations = CONFIG.password_iterations(); user.set_password(password, None, false, None); @@ -207,17 +226,6 @@ async fn _password_login( } } - // Check if the user is disabled - if !user.enabled { - err!( - "This user has been disabled", - format!("IP: {}. Username: {}.", ip.ip, username), - ErrorEvent { - event: EventType::UserFailedLogIn - } - ) - } - let now = Utc::now().naive_utc(); if user.verified_at.is_none() && CONFIG.mail_enabled() && CONFIG.signups_verify() { From 294b429436a1159ddb9b796b583fa79a9d04ef87 Mon Sep 17 00:00:00 2001 From: Mathijs van Veluw Date: Mon, 11 Nov 2024 20:14:04 +0100 Subject: [PATCH 006/286] Add dynamic CSS support (#4940) * Add dynamic CSS support Together with https://github.com/dani-garcia/bw_web_builds/pull/180 this PR will add support for dynamic CSS changes. For example, we could hide the register link if signups are not allowed. In the future show or hide the SSO button depending on if it is enabled or not. There also is a special `user.vaultwarden.scss` file so that users can add custom CSS without the need to modify the default (static) changes. This will prevent future changes from not being applied and still have the custom user changes to be added. Also added a special redirect when someone goes directly to `/index.html` as that might cause issues with loading other scripts and files. Signed-off-by: BlackDex * Add versions and fallback to built-in - Add both Vaultwarden and web-vault versions to the css_options. - Fallback to the inner templates if rendering or compiling the scss fails. This ensures the basics are always working even if someone breaks the templates. Signed-off-by: BlackDex * Fix fallback code to actually work The fallback now works by using an alternative `reg!` macro. This adds an extra template register which prefixes the template with `fallback_`. Signed-off-by: BlackDex * Updated the wiki link in the user template --------- Signed-off-by: BlackDex --- Cargo.lock | 43 +++++++ Cargo.toml | 3 + src/api/web.rs | 103 ++++++++++++++++- src/config.rs | 15 ++- .../templates/scss/user.vaultwarden.scss.hbs | 1 + .../templates/scss/vaultwarden.scss.hbs | 105 ++++++++++++++++++ 6 files changed, 266 insertions(+), 4 deletions(-) create mode 100644 src/static/templates/scss/user.vaultwarden.scss.hbs create mode 100644 src/static/templates/scss/vaultwarden.scss.hbs diff --git a/Cargo.lock b/Cargo.lock index ae469d23..9edd20bf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -552,6 +552,12 @@ dependencies = [ "stacker", ] +[[package]] +name = "codemap" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e769b5c8c8283982a987c6e948e540254f1058d5a74b8794914d4ef5fc2a24" + [[package]] name = "concurrent-queue" version = "2.5.0" @@ -1231,6 +1237,19 @@ dependencies = [ "spinning_top", ] +[[package]] +name = "grass_compiler" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d9e3df7f0222ce5184154973d247c591d9aadc28ce7a73c6cd31100c9facff6" +dependencies = [ + "codemap", + "indexmap", + "lasso", + "once_cell", + "phf", +] + [[package]] name = "h2" version = "0.3.26" @@ -1886,6 +1905,15 @@ dependencies = [ "log", ] +[[package]] +name = "lasso" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e14eda50a3494b3bf7b9ce51c52434a761e383d7238ce1dd5dcec2fbc13e9fb" +dependencies = [ + "hashbrown 0.14.5", +] + [[package]] name = "lazy_static" version = "1.5.0" @@ -2484,6 +2512,7 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" dependencies = [ + "phf_macros", "phf_shared", ] @@ -2507,6 +2536,19 @@ dependencies = [ "rand", ] +[[package]] +name = "phf_macros" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "phf_shared" version = "0.11.2" @@ -4056,6 +4098,7 @@ dependencies = [ "fern", "futures", "governor", + "grass_compiler", "handlebars", "hickory-resolver", "html5gum", diff --git a/Cargo.toml b/Cargo.toml index 78a1a4b5..150b3b9d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -163,6 +163,9 @@ argon2 = "0.5.3" # Reading a password from the cli for generating the Argon2id ADMIN_TOKEN rpassword = "7.3.1" +# Loading a dynamic CSS Stylesheet +grass_compiler = { version = "0.13.4", default-features = false } + # Strip debuginfo from the release builds # The symbols are the provide better panic traces # Also enable fat LTO and use 1 codegen unit for optimizations diff --git a/src/api/web.rs b/src/api/web.rs index 6983719b..a96d7e2a 100644 --- a/src/api/web.rs +++ b/src/api/web.rs @@ -1,13 +1,20 @@ +use once_cell::sync::Lazy; use std::path::{Path, PathBuf}; -use rocket::{fs::NamedFile, http::ContentType, response::content::RawHtml as Html, serde::json::Json, Catcher, Route}; +use rocket::{ + fs::NamedFile, + http::ContentType, + response::{content::RawCss as Css, content::RawHtml as Html, Redirect}, + serde::json::Json, + Catcher, Route, +}; use serde_json::Value; use crate::{ api::{core::now, ApiResult, EmptyResult}, auth::decode_file_download, error::Error, - util::{Cached, SafeString}, + util::{get_web_vault_version, Cached, SafeString}, CONFIG, }; @@ -16,7 +23,7 @@ pub fn routes() -> Vec { // crate::utils::LOGGED_ROUTES to make sure they appear in the log let mut routes = routes![attachments, alive, alive_head, static_files]; if CONFIG.web_vault_enabled() { - routes.append(&mut routes![web_index, web_index_head, app_id, web_files]); + routes.append(&mut routes![web_index, web_index_direct, web_index_head, app_id, web_files, vaultwarden_css]); } #[cfg(debug_assertions)] @@ -45,11 +52,101 @@ fn not_found() -> ApiResult> { Ok(Html(text)) } +#[get("/css/vaultwarden.css")] +fn vaultwarden_css() -> Cached> { + // Configure the web-vault version as an integer so it can be used as a comparison smaller or greater then. + // The default is based upon the version since this feature is added. + static WEB_VAULT_VERSION: Lazy = Lazy::new(|| { + let re = regex::Regex::new(r"(\d{4})\.(\d{1,2})\.(\d{1,2})").unwrap(); + let vault_version = get_web_vault_version(); + + let (major, minor, patch) = match re.captures(&vault_version) { + Some(c) if c.len() == 4 => ( + c.get(1).unwrap().as_str().parse().unwrap(), + c.get(2).unwrap().as_str().parse().unwrap(), + c.get(3).unwrap().as_str().parse().unwrap(), + ), + _ => (2024, 6, 2), + }; + format!("{major}{minor:02}{patch:02}").parse::().unwrap() + }); + + // Configure the Vaultwarden version as an integer so it can be used as a comparison smaller or greater then. + // The default is based upon the version since this feature is added. + static VW_VERSION: Lazy = Lazy::new(|| { + let re = regex::Regex::new(r"(\d{1})\.(\d{1,2})\.(\d{1,2})").unwrap(); + let vw_version = crate::VERSION.unwrap_or("1.32.1"); + + let (major, minor, patch) = match re.captures(vw_version) { + Some(c) if c.len() == 4 => ( + c.get(1).unwrap().as_str().parse().unwrap(), + c.get(2).unwrap().as_str().parse().unwrap(), + c.get(3).unwrap().as_str().parse().unwrap(), + ), + _ => (1, 32, 1), + }; + format!("{major}{minor:02}{patch:02}").parse::().unwrap() + }); + + let css_options = json!({ + "web_vault_version": *WEB_VAULT_VERSION, + "vw_version": *VW_VERSION, + "signup_disabled": !CONFIG.signups_allowed() && CONFIG.signups_domains_whitelist().is_empty(), + "mail_enabled": CONFIG.mail_enabled(), + "yubico_enabled": CONFIG._enable_yubico() && (CONFIG.yubico_client_id().is_some() == CONFIG.yubico_secret_key().is_some()), + "emergency_access_allowed": CONFIG.emergency_access_allowed(), + "sends_allowed": CONFIG.sends_allowed(), + "load_user_scss": true, + }); + + let scss = match CONFIG.render_template("scss/vaultwarden.scss", &css_options) { + Ok(t) => t, + Err(e) => { + // Something went wrong loading the template. Use the fallback + warn!("Loading scss/vaultwarden.scss.hbs or scss/user.vaultwarden.scss.hbs failed. {e}"); + CONFIG + .render_fallback_template("scss/vaultwarden.scss", &css_options) + .expect("Fallback scss/vaultwarden.scss.hbs to render") + } + }; + + let css = match grass_compiler::from_string( + scss, + &grass_compiler::Options::default().style(grass_compiler::OutputStyle::Compressed), + ) { + Ok(css) => css, + Err(e) => { + // Something went wrong compiling the scss. Use the fallback + warn!("Compiling the Vaultwarden SCSS styles failed. {e}"); + let mut css_options = css_options; + css_options["load_user_scss"] = json!(false); + let scss = CONFIG + .render_fallback_template("scss/vaultwarden.scss", &css_options) + .expect("Fallback scss/vaultwarden.scss.hbs to render"); + grass_compiler::from_string( + scss, + &grass_compiler::Options::default().style(grass_compiler::OutputStyle::Compressed), + ) + .expect("SCSS to compile") + } + }; + + // Cache for one day should be enough and not too much + Cached::ttl(Css(css), 86_400, false) +} + #[get("/")] async fn web_index() -> Cached> { Cached::short(NamedFile::open(Path::new(&CONFIG.web_vault_folder()).join("index.html")).await.ok(), false) } +// Make sure that `/index.html` redirect to actual domain path. +// If not, this might cause issues with the web-vault +#[get("/index.html")] +fn web_index_direct() -> Redirect { + Redirect::to(format!("{}/", CONFIG.domain_path())) +} + #[head("/")] fn web_index_head() -> EmptyResult { // Add an explicit HEAD route to prevent uptime monitoring services from diff --git a/src/config.rs b/src/config.rs index aa6b1145..61a47b76 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1269,11 +1269,16 @@ impl Config { let hb = load_templates(CONFIG.templates_folder()); hb.render(name, data).map_err(Into::into) } else { - let hb = &CONFIG.inner.read().unwrap().templates; + let hb = &self.inner.read().unwrap().templates; hb.render(name, data).map_err(Into::into) } } + pub fn render_fallback_template(&self, name: &str, data: &T) -> Result { + let hb = &self.inner.read().unwrap().templates; + hb.render(&format!("fallback_{name}"), data).map_err(Into::into) + } + pub fn set_rocket_shutdown_handle(&self, handle: rocket::Shutdown) { self.inner.write().unwrap().rocket_shutdown_handle = Some(handle); } @@ -1312,6 +1317,11 @@ where reg!($name); reg!(concat!($name, $ext)); }}; + (@withfallback $name:expr) => {{ + let template = include_str!(concat!("static/templates/", $name, ".hbs")); + hb.register_template_string($name, template).unwrap(); + hb.register_template_string(concat!("fallback_", $name), template).unwrap(); + }}; } // First register default templates here @@ -1355,6 +1365,9 @@ where reg!("404"); + reg!(@withfallback "scss/vaultwarden.scss"); + reg!("scss/user.vaultwarden.scss"); + // And then load user templates to overwrite the defaults // Use .hbs extension for the files // Templates get registered with their relative name diff --git a/src/static/templates/scss/user.vaultwarden.scss.hbs b/src/static/templates/scss/user.vaultwarden.scss.hbs new file mode 100644 index 00000000..c0b8ed2a --- /dev/null +++ b/src/static/templates/scss/user.vaultwarden.scss.hbs @@ -0,0 +1 @@ +/* See the wiki for examples and details: https://github.com/dani-garcia/vaultwarden/wiki/Customize-Vaultwarden-CSS */ diff --git a/src/static/templates/scss/vaultwarden.scss.hbs b/src/static/templates/scss/vaultwarden.scss.hbs new file mode 100644 index 00000000..3fc3e70e --- /dev/null +++ b/src/static/templates/scss/vaultwarden.scss.hbs @@ -0,0 +1,105 @@ +/**** START Static Vaultwarden changes ****/ +/* This combines all selectors extending it into one */ +%vw-hide { + display: none !important; +} + +/* This allows searching for the combined style in the browsers dev-tools (look into the head tag) */ +.vw-hide, +head { + @extend %vw-hide; +} + +/* Hide the Subscription Page tab */ +bit-nav-item[route="settings/subscription"] { + @extend %vw-hide; +} + +/* Hide any link pointing to Free Bitwarden Families */ +a[href$="/settings/sponsored-families"] { + @extend %vw-hide; +} + +/* Hide the `Enterprise Single Sign-On` button on the login page */ +a[routerlink="/sso"] { + @extend %vw-hide; +} + +/* Hide Two-Factor menu in Organization settings */ +bit-nav-item[route="settings/two-factor"], +a[href$="/settings/two-factor"] { + @extend %vw-hide; +} + +/* Hide Business Owned checkbox */ +app-org-info > form:nth-child(1) > div:nth-child(3) { + @extend %vw-hide; +} + +/* Hide the `This account is owned by a business` checkbox and label */ +#ownedBusiness, +label[for^="ownedBusiness"] { + @extend %vw-hide; +} + +/* Hide the radio button and label for the `Custom` org user type */ +#userTypeCustom, +label[for^="userTypeCustom"] { + @extend %vw-hide; +} + +/* Hide Business Name */ +app-org-account form div bit-form-field.tw-block:nth-child(3) { + @extend %vw-hide; +} + +/* Hide organization plans */ +app-organization-plans > form > bit-section:nth-child(2) { + @extend %vw-hide; +} + +/* Hide Device Verification form at the Two Step Login screen */ +app-security > app-two-factor-setup > form { + @extend %vw-hide; +} +/**** END Static Vaultwarden Changes ****/ +/**** START Dynamic Vaultwarden Changes ****/ +{{#if signup_disabled}} +/* Hide the register link on the login screen */ +app-frontend-layout > app-login > form > div > div > div > p { + @extend %vw-hide; +} +{{/if}} + +/* Hide `Email` 2FA if mail is not enabled */ +{{#unless mail_enabled}} +app-two-factor-setup ul.list-group.list-group-2fa li.list-group-item:nth-child(5) { + @extend %vw-hide; +} +{{/unless}} + +/* Hide `YubiKey OTP security key` 2FA if it is not enabled */ +{{#unless yubico_enabled}} +app-two-factor-setup ul.list-group.list-group-2fa li.list-group-item:nth-child(2) { + @extend %vw-hide; +} +{{/unless}} + +/* Hide Emergency Access if not allowed */ +{{#unless emergency_access_allowed}} +bit-nav-item[route="settings/emergency-access"] { + @extend %vw-hide; +} +{{/unless}} + +/* Hide Sends if not allowed */ +{{#unless sends_allowed}} +bit-nav-item[route="sends"] { + @extend %vw-hide; +} +{{/unless}} +/**** End Dynamic Vaultwarden Changes ****/ +/**** Include a special user stylesheet for custom changes ****/ +{{#if load_user_scss}} +{{> scss/user.vaultwarden.scss }} +{{/if}} From ba48ca68fc165be704af50171242f656e79fe685 Mon Sep 17 00:00:00 2001 From: Mathijs van Veluw Date: Tue, 12 Nov 2024 11:09:28 +0100 Subject: [PATCH 007/286] fix hibp username encoding and pw hint check (#5180) * fix hibp username encoding Signed-off-by: BlackDex * Fix password-hint check Signed-off-by: BlackDex --------- Signed-off-by: BlackDex --- src/api/core/accounts.rs | 2 +- src/api/core/mod.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/api/core/accounts.rs b/src/api/core/accounts.rs index e6654add..4e566bc9 100644 --- a/src/api/core/accounts.rs +++ b/src/api/core/accounts.rs @@ -842,7 +842,7 @@ struct PasswordHintData { #[post("/accounts/password-hint", data = "")] async fn password_hint(data: Json, mut conn: DbConn) -> EmptyResult { - if !CONFIG.mail_enabled() && !CONFIG.show_password_hint() { + if !CONFIG.mail_enabled() || !CONFIG.show_password_hint() { err!("This server is not configured to provide password hints."); } diff --git a/src/api/core/mod.rs b/src/api/core/mod.rs index 1638afe5..75c63c16 100644 --- a/src/api/core/mod.rs +++ b/src/api/core/mod.rs @@ -136,8 +136,8 @@ async fn put_eq_domains(data: Json, headers: Headers, conn: DbC #[get("/hibp/breach?")] async fn hibp_breach(username: &str, _headers: Headers) -> JsonResult { + let username: String = url::form_urlencoded::byte_serialize(username.as_bytes()).collect(); if let Some(api_key) = crate::CONFIG.hibp_api_key() { - let username: String = url::form_urlencoded::byte_serialize(username.as_bytes()).collect(); let url = format!( "https://haveibeenpwned.com/api/v3/breachedaccount/{username}?truncateResponse=false&includeUnverified=false" ); From e927b8aa5ec8f0352bfb2ff95a996a89389959ff Mon Sep 17 00:00:00 2001 From: Mathijs van Veluw Date: Tue, 12 Nov 2024 15:48:39 +0100 Subject: [PATCH 008/286] Remove auth-request deletion (#5184) 2FA is needed to login even when using login-with-device. If the user didn't saved the 2FA token they still need to provide this. We deleted the auth-request after validation the request, but before 2FA was triggered. Removing the deletion of this record from that point as it will get cleaned-up automatically anyways. Signed-off-by: BlackDex --- src/api/identity.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/api/identity.rs b/src/api/identity.rs index 003e4d97..f2618164 100644 --- a/src/api/identity.rs +++ b/src/api/identity.rs @@ -190,9 +190,6 @@ async fn _password_login( ) }; - // Delete the request after we used it - auth_request.delete(conn).await?; - if auth_request.user_uuid != user.uuid || !auth_request.approved.unwrap_or(false) || ip.ip.to_string() != auth_request.request_ip From adb21d5c1acfef9bd06d1ad9cdf3b916b38b201b Mon Sep 17 00:00:00 2001 From: Stefan Melmuk <509385+stefan0xC@users.noreply.github.com> Date: Tue, 12 Nov 2024 21:22:25 +0100 Subject: [PATCH 009/286] fix password hint check (#5189) * fix password hint check don't show password hints if you have disabled the hints with PASSWORD_HINTS_ALLOWED=false or if you have not configured mail and opted into showing password hints * update descriptions for pw hints options --- .env.template | 7 ++++--- src/api/core/accounts.rs | 2 +- src/config.rs | 8 ++++---- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/.env.template b/.env.template index 5a8686d5..075689e9 100644 --- a/.env.template +++ b/.env.template @@ -280,12 +280,13 @@ ## The default for new users. If changed, it will be updated during login for existing users. # PASSWORD_ITERATIONS=600000 -## Controls whether users can set password hints. This setting applies globally to all users. +## Controls whether users can set or show password hints. This setting applies globally to all users. # PASSWORD_HINTS_ALLOWED=true ## Controls whether a password hint should be shown directly in the web page if -## SMTP service is not configured. Not recommended for publicly-accessible instances -## as this provides unauthenticated access to potentially sensitive data. +## SMTP service is not configured and password hints are allowed. +## Not recommended for publicly-accessible instances because this provides +## unauthenticated access to potentially sensitive data. # SHOW_PASSWORD_HINT=false ######################### diff --git a/src/api/core/accounts.rs b/src/api/core/accounts.rs index 4e566bc9..7c3919ad 100644 --- a/src/api/core/accounts.rs +++ b/src/api/core/accounts.rs @@ -842,7 +842,7 @@ struct PasswordHintData { #[post("/accounts/password-hint", data = "")] async fn password_hint(data: Json, mut conn: DbConn) -> EmptyResult { - if !CONFIG.mail_enabled() || !CONFIG.show_password_hint() { + if !CONFIG.password_hints_allowed() || (!CONFIG.mail_enabled() && !CONFIG.show_password_hint()) { err!("This server is not configured to provide password hints."); } diff --git a/src/config.rs b/src/config.rs index 61a47b76..244499d0 100644 --- a/src/config.rs +++ b/src/config.rs @@ -497,11 +497,11 @@ make_config! { /// Password iterations |> Number of server-side passwords hashing iterations for the password hash. /// The default for new users. If changed, it will be updated during login for existing users. password_iterations: i32, true, def, 600_000; - /// Allow password hints |> Controls whether users can set password hints. This setting applies globally to all users. + /// Allow password hints |> Controls whether users can set or show password hints. This setting applies globally to all users. password_hints_allowed: bool, true, def, true; - /// Show password hint |> Controls whether a password hint should be shown directly in the web page - /// if SMTP service is not configured. Not recommended for publicly-accessible instances as this - /// provides unauthenticated access to potentially sensitive data. + /// Show password hint (Know the risks!) |> Controls whether a password hint should be shown directly in the web page + /// if SMTP service is not configured and password hints are allowed. Not recommended for publicly-accessible instances + /// because this provides unauthenticated access to potentially sensitive data. show_password_hint: bool, true, def, false; /// Admin token/Argon2 PHC |> The plain text token or Argon2 PHC string used to authenticate in this very same page. Changing it here will not deauthorize the current session! From ff33534c07ba05184fbb2adf562334ac56686c55 Mon Sep 17 00:00:00 2001 From: Stefan Melmuk <509385+stefan0xC@users.noreply.github.com> Date: Wed, 13 Nov 2024 19:19:19 +0100 Subject: [PATCH 010/286] don't infer manage permission for groups (#5190) the web-vault v2024.6.2 currently cannot deal with manage permission so instead of relying on the org user type this should just default to false --- src/api/core/organizations.rs | 8 ++++---- src/db/models/group.rs | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/api/core/organizations.rs b/src/api/core/organizations.rs index 402e7617..96ff9805 100644 --- a/src/api/core/organizations.rs +++ b/src/api/core/organizations.rs @@ -2305,14 +2305,14 @@ async fn _restore_organization_user( } #[get("/organizations//groups")] -async fn get_groups(org_id: &str, headers: ManagerHeadersLoose, mut conn: DbConn) -> JsonResult { +async fn get_groups(org_id: &str, _headers: ManagerHeadersLoose, mut conn: DbConn) -> JsonResult { let groups: Vec = if CONFIG.org_groups_enabled() { // Group::find_by_organization(&org_id, &mut conn).await.iter().map(Group::to_json).collect::() let groups = Group::find_by_organization(org_id, &mut conn).await; let mut groups_json = Vec::with_capacity(groups.len()); for g in groups { - groups_json.push(g.to_json_details(&headers.org_user.atype, &mut conn).await) + groups_json.push(g.to_json_details(&mut conn).await) } groups_json } else { @@ -2500,7 +2500,7 @@ async fn add_update_group( } #[get("/organizations/<_org_id>/groups//details")] -async fn get_group_details(_org_id: &str, group_id: &str, headers: AdminHeaders, mut conn: DbConn) -> JsonResult { +async fn get_group_details(_org_id: &str, group_id: &str, _headers: AdminHeaders, mut conn: DbConn) -> JsonResult { if !CONFIG.org_groups_enabled() { err!("Group support is disabled"); } @@ -2510,7 +2510,7 @@ async fn get_group_details(_org_id: &str, group_id: &str, headers: AdminHeaders, _ => err!("Group could not be found!"), }; - Ok(Json(group.to_json_details(&(headers.org_user_type as i32), &mut conn).await)) + Ok(Json(group.to_json_details(&mut conn).await)) } #[post("/organizations//groups//delete")] diff --git a/src/db/models/group.rs b/src/db/models/group.rs index 66ad338a..e226512d 100644 --- a/src/db/models/group.rs +++ b/src/db/models/group.rs @@ -1,4 +1,4 @@ -use super::{User, UserOrgType, UserOrganization}; +use super::{User, UserOrganization}; use crate::api::EmptyResult; use crate::db::DbConn; use crate::error::MapResult; @@ -73,7 +73,7 @@ impl Group { }) } - pub async fn to_json_details(&self, user_org_type: &i32, conn: &mut DbConn) -> Value { + pub async fn to_json_details(&self, conn: &mut DbConn) -> Value { let collections_groups: Vec = CollectionGroup::find_by_group(&self.uuid, conn) .await .iter() @@ -82,7 +82,7 @@ impl Group { "id": entry.collections_uuid, "readOnly": entry.read_only, "hidePasswords": entry.hide_passwords, - "manage": *user_org_type >= UserOrgType::Admin || (*user_org_type == UserOrgType::Manager && !entry.read_only && !entry.hide_passwords) + "manage": false }) }) .collect(); From 0d16b38a68c702e7f300a64f9e55d897916ae238 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garc=C3=ADa?= Date: Fri, 15 Nov 2024 11:25:51 +0100 Subject: [PATCH 011/286] Some more authrequest changes (#5188) --- src/api/core/accounts.rs | 10 ++++++++-- src/api/identity.rs | 4 ++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/api/core/accounts.rs b/src/api/core/accounts.rs index 7c3919ad..1d3bcf37 100644 --- a/src/api/core/accounts.rs +++ b/src/api/core/accounts.rs @@ -1190,11 +1190,19 @@ async fn put_auth_request( err!("AuthRequest doesn't exist", "User uuid's do not match") } + if auth_request.approved.is_some() { + err!("An authentication request with the same device already exists") + } + + let response_date = Utc::now().naive_utc(); + let response_date_utc = format_date(&response_date); + if data.request_approved { auth_request.approved = Some(data.request_approved); auth_request.enc_key = Some(data.key); auth_request.master_password_hash = data.master_password_hash; auth_request.response_device_id = Some(data.device_identifier.clone()); + auth_request.response_date = Some(response_date); auth_request.save(&mut conn).await?; ant.send_auth_response(&auth_request.user_uuid, &auth_request.uuid).await; @@ -1204,8 +1212,6 @@ async fn put_auth_request( auth_request.delete(&mut conn).await?; } - let response_date_utc = auth_request.response_date.map(|response_date| format_date(&response_date)); - Ok(Json(json!({ "id": uuid, "publicKey": auth_request.public_key, diff --git a/src/api/identity.rs b/src/api/identity.rs index f2618164..445d61fd 100644 --- a/src/api/identity.rs +++ b/src/api/identity.rs @@ -190,8 +190,12 @@ async fn _password_login( ) }; + let expiration_time = auth_request.creation_date + chrono::Duration::minutes(5); + let request_expired = Utc::now().naive_utc() >= expiration_time; + if auth_request.user_uuid != user.uuid || !auth_request.approved.unwrap_or(false) + || request_expired || ip.ip.to_string() != auth_request.request_ip || !auth_request.check_access_code(password) { From 2393c3f3c08f451a04643fd9fed5027f491dc12d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garc=C3=ADa?= Date: Fri, 15 Nov 2024 18:38:16 +0100 Subject: [PATCH 012/286] Support SSH keys on desktop 2024.12 (#5187) * Support SSH keys on desktop 2024.12 * Document flags in .env.template * Validate key rotation contents --- .env.template | 2 + src/api/core/accounts.rs | 111 ++++++++++++++++++++++++++-------- src/api/core/ciphers.rs | 26 +++++++- src/api/core/organizations.rs | 18 +++--- src/auth.rs | 24 +++++++- src/config.rs | 11 +++- src/db/models/cipher.rs | 5 +- 7 files changed, 156 insertions(+), 41 deletions(-) diff --git a/.env.template b/.env.template index 075689e9..62ce5258 100644 --- a/.env.template +++ b/.env.template @@ -350,6 +350,8 @@ ## - "browser-fileless-import": Directly import credentials from other providers without a file. ## - "extension-refresh": Temporarily enable the new extension design until general availability (should be used with the beta Chrome extension) ## - "fido2-vault-credentials": Enable the use of FIDO2 security keys as second factor. +## - "ssh-key-vault-item": Enable the creation and use of SSH key vault items. (Needs clients >=2024.12.0) +## - "ssh-agent": Enable SSH agent support on Desktop. (Needs desktop >=2024.12.0) # EXPERIMENTAL_CLIENT_FEATURE_FLAGS=fido2-vault-credentials ## Require new device emails. When a user logs in an email is required to be sent. diff --git a/src/api/core/accounts.rs b/src/api/core/accounts.rs index 1d3bcf37..87e44529 100644 --- a/src/api/core/accounts.rs +++ b/src/api/core/accounts.rs @@ -1,3 +1,5 @@ +use std::collections::HashSet; + use crate::db::DbPool; use chrono::Utc; use rocket::serde::json::Json; @@ -477,6 +479,60 @@ struct KeyData { private_key: String, } +fn validate_keydata( + data: &KeyData, + existing_ciphers: &[Cipher], + existing_folders: &[Folder], + existing_emergency_access: &[EmergencyAccess], + existing_user_orgs: &[UserOrganization], + existing_sends: &[Send], +) -> EmptyResult { + // Check that we're correctly rotating all the user's ciphers + let existing_cipher_ids = existing_ciphers.iter().map(|c| c.uuid.as_str()).collect::>(); + let provided_cipher_ids = data + .ciphers + .iter() + .filter(|c| c.organization_id.is_none()) + .filter_map(|c| c.id.as_deref()) + .collect::>(); + if !provided_cipher_ids.is_superset(&existing_cipher_ids) { + err!("All existing ciphers must be included in the rotation") + } + + // Check that we're correctly rotating all the user's folders + let existing_folder_ids = existing_folders.iter().map(|f| f.uuid.as_str()).collect::>(); + let provided_folder_ids = data.folders.iter().filter_map(|f| f.id.as_deref()).collect::>(); + if !provided_folder_ids.is_superset(&existing_folder_ids) { + err!("All existing folders must be included in the rotation") + } + + // Check that we're correctly rotating all the user's emergency access keys + let existing_emergency_access_ids = + existing_emergency_access.iter().map(|ea| ea.uuid.as_str()).collect::>(); + let provided_emergency_access_ids = + data.emergency_access_keys.iter().map(|ea| ea.id.as_str()).collect::>(); + if !provided_emergency_access_ids.is_superset(&existing_emergency_access_ids) { + err!("All existing emergency access keys must be included in the rotation") + } + + // Check that we're correctly rotating all the user's reset password keys + let existing_reset_password_ids = existing_user_orgs.iter().map(|uo| uo.org_uuid.as_str()).collect::>(); + let provided_reset_password_ids = + data.reset_password_keys.iter().map(|rp| rp.organization_id.as_str()).collect::>(); + if !provided_reset_password_ids.is_superset(&existing_reset_password_ids) { + err!("All existing reset password keys must be included in the rotation") + } + + // Check that we're correctly rotating all the user's sends + let existing_send_ids = existing_sends.iter().map(|s| s.uuid.as_str()).collect::>(); + let provided_send_ids = data.sends.iter().filter_map(|s| s.id.as_deref()).collect::>(); + if !provided_send_ids.is_superset(&existing_send_ids) { + err!("All existing sends must be included in the rotation") + } + + Ok(()) +} + #[post("/accounts/key", data = "")] async fn post_rotatekey(data: Json, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult { // TODO: See if we can wrap everything within a SQL Transaction. If something fails it should revert everything. @@ -494,20 +550,35 @@ async fn post_rotatekey(data: Json, headers: Headers, mut conn: DbConn, let user_uuid = &headers.user.uuid; + // TODO: Ideally we'd do everything after this point in a single transaction. + + let mut existing_ciphers = Cipher::find_owned_by_user(user_uuid, &mut conn).await; + let mut existing_folders = Folder::find_by_user(user_uuid, &mut conn).await; + let mut existing_emergency_access = EmergencyAccess::find_all_by_grantor_uuid(user_uuid, &mut conn).await; + let mut existing_user_orgs = UserOrganization::find_by_user(user_uuid, &mut conn).await; + // We only rotate the reset password key if it is set. + existing_user_orgs.retain(|uo| uo.reset_password_key.is_some()); + let mut existing_sends = Send::find_by_user(user_uuid, &mut conn).await; + + validate_keydata( + &data, + &existing_ciphers, + &existing_folders, + &existing_emergency_access, + &existing_user_orgs, + &existing_sends, + )?; + // Update folder data for folder_data in data.folders { // Skip `null` folder id entries. // See: https://github.com/bitwarden/clients/issues/8453 if let Some(folder_id) = folder_data.id { - let mut saved_folder = match Folder::find_by_uuid(&folder_id, &mut conn).await { + let saved_folder = match existing_folders.iter_mut().find(|f| f.uuid == folder_id) { Some(folder) => folder, None => err!("Folder doesn't exist"), }; - if &saved_folder.user_uuid != user_uuid { - err!("The folder is not owned by the user") - } - saved_folder.name = folder_data.name; saved_folder.save(&mut conn).await? } @@ -515,9 +586,8 @@ async fn post_rotatekey(data: Json, headers: Headers, mut conn: DbConn, // Update emergency access data for emergency_access_data in data.emergency_access_keys { - let mut saved_emergency_access = - match EmergencyAccess::find_by_uuid_and_grantor_uuid(&emergency_access_data.id, user_uuid, &mut conn).await - { + let saved_emergency_access = + match existing_emergency_access.iter_mut().find(|ea| ea.uuid == emergency_access_data.id) { Some(emergency_access) => emergency_access, None => err!("Emergency access doesn't exist or is not owned by the user"), }; @@ -528,13 +598,11 @@ async fn post_rotatekey(data: Json, headers: Headers, mut conn: DbConn, // Update reset password data for reset_password_data in data.reset_password_keys { - let mut user_org = - match UserOrganization::find_by_user_and_org(user_uuid, &reset_password_data.organization_id, &mut conn) - .await - { - Some(reset_password) => reset_password, - None => err!("Reset password doesn't exist"), - }; + let user_org = match existing_user_orgs.iter_mut().find(|uo| uo.org_uuid == reset_password_data.organization_id) + { + Some(reset_password) => reset_password, + None => err!("Reset password doesn't exist"), + }; user_org.reset_password_key = Some(reset_password_data.reset_password_key); user_org.save(&mut conn).await? @@ -542,12 +610,12 @@ async fn post_rotatekey(data: Json, headers: Headers, mut conn: DbConn, // Update send data for send_data in data.sends { - let mut send = match Send::find_by_uuid(send_data.id.as_ref().unwrap(), &mut conn).await { + let send = match existing_sends.iter_mut().find(|s| &s.uuid == send_data.id.as_ref().unwrap()) { Some(send) => send, None => err!("Send doesn't exist"), }; - update_send_from_data(&mut send, send_data, &headers, &mut conn, &nt, UpdateType::None).await?; + update_send_from_data(send, send_data, &headers, &mut conn, &nt, UpdateType::None).await?; } // Update cipher data @@ -555,20 +623,15 @@ async fn post_rotatekey(data: Json, headers: Headers, mut conn: DbConn, for cipher_data in data.ciphers { if cipher_data.organization_id.is_none() { - let mut saved_cipher = match Cipher::find_by_uuid(cipher_data.id.as_ref().unwrap(), &mut conn).await { + let saved_cipher = match existing_ciphers.iter_mut().find(|c| &c.uuid == cipher_data.id.as_ref().unwrap()) { Some(cipher) => cipher, None => err!("Cipher doesn't exist"), }; - if saved_cipher.user_uuid.as_ref().unwrap() != user_uuid { - err!("The cipher is not owned by the user") - } - // Prevent triggering cipher updates via WebSockets by settings UpdateType::None // The user sessions are invalidated because all the ciphers were re-encrypted and thus triggering an update could cause issues. // We force the users to logout after the user has been saved to try and prevent these issues. - update_cipher_from_data(&mut saved_cipher, cipher_data, &headers, None, &mut conn, &nt, UpdateType::None) - .await? + update_cipher_from_data(saved_cipher, cipher_data, &headers, None, &mut conn, &nt, UpdateType::None).await? } } diff --git a/src/api/core/ciphers.rs b/src/api/core/ciphers.rs index 94bdc485..57d069b8 100644 --- a/src/api/core/ciphers.rs +++ b/src/api/core/ciphers.rs @@ -10,6 +10,7 @@ use rocket::{ }; use serde_json::Value; +use crate::auth::ClientVersion; use crate::util::NumberOrString; use crate::{ api::{self, core::log_event, EmptyResult, JsonResult, Notify, PasswordOrOtpData, UpdateType}, @@ -104,11 +105,27 @@ struct SyncData { } #[get("/sync?")] -async fn sync(data: SyncData, headers: Headers, mut conn: DbConn) -> Json { +async fn sync( + data: SyncData, + headers: Headers, + client_version: Option, + mut conn: DbConn, +) -> Json { let user_json = headers.user.to_json(&mut conn).await; // Get all ciphers which are visible by the user - let ciphers = Cipher::find_by_user_visible(&headers.user.uuid, &mut conn).await; + let mut ciphers = Cipher::find_by_user_visible(&headers.user.uuid, &mut conn).await; + + // Filter out SSH keys if the client version is less than 2024.12.0 + let show_ssh_keys = if let Some(client_version) = client_version { + let ver_match = semver::VersionReq::parse(">=2024.12.0").unwrap(); + ver_match.matches(&client_version.0) + } else { + false + }; + if !show_ssh_keys { + ciphers.retain(|c| c.atype != 5); + } let cipher_sync_data = CipherSyncData::new(&headers.user.uuid, CipherSyncType::User, &mut conn).await; @@ -216,7 +233,8 @@ pub struct CipherData { Login = 1, SecureNote = 2, Card = 3, - Identity = 4 + Identity = 4, + SshKey = 5 */ pub r#type: i32, pub name: String, @@ -228,6 +246,7 @@ pub struct CipherData { secure_note: Option, card: Option, identity: Option, + ssh_key: Option, favorite: Option, reprompt: Option, @@ -469,6 +488,7 @@ pub async fn update_cipher_from_data( 2 => data.secure_note, 3 => data.card, 4 => data.identity, + 5 => data.ssh_key, _ => err!("Invalid type"), }; diff --git a/src/api/core/organizations.rs b/src/api/core/organizations.rs index 96ff9805..7ee6a089 100644 --- a/src/api/core/organizations.rs +++ b/src/api/core/organizations.rs @@ -9,7 +9,7 @@ use crate::{ core::{log_event, two_factor, CipherSyncData, CipherSyncType}, EmptyResult, JsonResult, Notify, PasswordOrOtpData, UpdateType, }, - auth::{decode_invite, AdminHeaders, Headers, ManagerHeaders, ManagerHeadersLoose, OwnerHeaders}, + auth::{decode_invite, AdminHeaders, ClientVersion, Headers, ManagerHeaders, ManagerHeadersLoose, OwnerHeaders}, db::{models::*, DbConn}, error::Error, mail, @@ -2999,18 +2999,20 @@ async fn put_reset_password_enrollment( // We need to convert all keys so they have the first character to be a lowercase. // Else the export will be just an empty JSON file. #[get("/organizations//export")] -async fn get_org_export(org_id: &str, headers: AdminHeaders, mut conn: DbConn) -> Json { - use semver::{Version, VersionReq}; - +async fn get_org_export( + org_id: &str, + headers: AdminHeaders, + client_version: Option, + mut conn: DbConn, +) -> Json { // Since version v2023.1.0 the format of the export is different. // Also, this endpoint was created since v2022.9.0. // Therefore, we will check for any version smaller then v2023.1.0 and return a different response. // If we can't determine the version, we will use the latest default v2023.1.0 and higher. // https://github.com/bitwarden/server/blob/9ca93381ce416454734418c3a9f99ab49747f1b6/src/Api/Controllers/OrganizationExportController.cs#L44 - let use_list_response_model = if let Some(client_version) = headers.client_version { - let ver_match = VersionReq::parse("<2023.1.0").unwrap(); - let client_version = Version::parse(&client_version).unwrap(); - ver_match.matches(&client_version) + let use_list_response_model = if let Some(client_version) = client_version { + let ver_match = semver::VersionReq::parse("<2023.1.0").unwrap(); + ver_match.matches(&client_version.0) } else { false }; diff --git a/src/auth.rs b/src/auth.rs index b1a743da..809ef9fd 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -615,7 +615,6 @@ pub struct AdminHeaders { pub device: Device, pub user: User, pub org_user_type: UserOrgType, - pub client_version: Option, pub ip: ClientIp, } @@ -625,14 +624,12 @@ impl<'r> FromRequest<'r> for AdminHeaders { async fn from_request(request: &'r Request<'_>) -> Outcome { let headers = try_outcome!(OrgHeaders::from_request(request).await); - let client_version = request.headers().get_one("Bitwarden-Client-Version").map(String::from); if headers.org_user_type >= UserOrgType::Admin { Outcome::Success(Self { host: headers.host, device: headers.device, user: headers.user, org_user_type: headers.org_user_type, - client_version, ip: headers.ip, }) } else { @@ -900,3 +897,24 @@ impl<'r> FromRequest<'r> for WsAccessTokenHeader { }) } } + +pub struct ClientVersion(pub semver::Version); + +#[rocket::async_trait] +impl<'r> FromRequest<'r> for ClientVersion { + type Error = &'static str; + + async fn from_request(request: &'r Request<'_>) -> Outcome { + let headers = request.headers(); + + let Some(version) = headers.get_one("Bitwarden-Client-Version") else { + err_handler!("No Bitwarden-Client-Version header provided") + }; + + let Ok(version) = semver::Version::parse(version) else { + err_handler!("Invalid Bitwarden-Client-Version header provided") + }; + + Outcome::Success(ClientVersion(version)) + } +} diff --git a/src/config.rs b/src/config.rs index 244499d0..e4e80927 100644 --- a/src/config.rs +++ b/src/config.rs @@ -811,8 +811,15 @@ fn validate_config(cfg: &ConfigItems) -> Result<(), Error> { } // TODO: deal with deprecated flags so they can be removed from this list, cf. #4263 - const KNOWN_FLAGS: &[&str] = - &["autofill-overlay", "autofill-v2", "browser-fileless-import", "extension-refresh", "fido2-vault-credentials"]; + const KNOWN_FLAGS: &[&str] = &[ + "autofill-overlay", + "autofill-v2", + "browser-fileless-import", + "extension-refresh", + "fido2-vault-credentials", + "ssh-key-vault-item", + "ssh-agent", + ]; let configured_flags = parse_experimental_client_feature_flags(&cfg.experimental_client_feature_flags); let invalid_flags: Vec<_> = configured_flags.keys().filter(|flag| !KNOWN_FLAGS.contains(&flag.as_str())).collect(); if !invalid_flags.is_empty() { diff --git a/src/db/models/cipher.rs b/src/db/models/cipher.rs index 9c568284..75d54df2 100644 --- a/src/db/models/cipher.rs +++ b/src/db/models/cipher.rs @@ -30,7 +30,8 @@ db_object! { Login = 1, SecureNote = 2, Card = 3, - Identity = 4 + Identity = 4, + SshKey = 5 */ pub atype: i32, pub name: String, @@ -319,6 +320,7 @@ impl Cipher { "secureNote": null, "card": null, "identity": null, + "sshKey": null, }); // These values are only needed for user/default syncs @@ -347,6 +349,7 @@ impl Cipher { 2 => "secureNote", 3 => "card", 4 => "identity", + 5 => "sshKey", _ => panic!("Wrong type"), }; From cdfdc6ff4f61a7495cd70609c0d9098ff10b55a4 Mon Sep 17 00:00:00 2001 From: Mathijs van Veluw Date: Sun, 17 Nov 2024 21:33:23 +0100 Subject: [PATCH 013/286] Fix Org Import duplicate collections (#5200) This fixes an issue with collections be duplicated same as was an issue with folders. Also made some optimizations by using HashSet where possible and device the Vec/Hash capacity. And instead of passing objects only use the UUID which was the only value we needed. Also found an issue with importing a personal export via the Org import where folders are used. Since Org's do not use folder we needed to clear those out, same as Bitwarden does. Fixes #5193 Signed-off-by: BlackDex --- src/api/core/ciphers.rs | 10 ++++---- src/api/core/organizations.rs | 39 ++++++++++++++++------------- src/api/core/two_factor/duo_oidc.rs | 5 +--- 3 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/api/core/ciphers.rs b/src/api/core/ciphers.rs index 57d069b8..aa390e5e 100644 --- a/src/api/core/ciphers.rs +++ b/src/api/core/ciphers.rs @@ -222,7 +222,7 @@ pub struct CipherData { // Id is optional as it is included only in bulk share pub id: Option, // Folder id is not included in import - folder_id: Option, + pub folder_id: Option, // TODO: Some of these might appear all the time, no need for Option #[serde(alias = "organizationID")] pub organization_id: Option, @@ -585,11 +585,11 @@ async fn post_ciphers_import( Cipher::validate_cipher_data(&data.ciphers)?; // Read and create the folders - let existing_folders: Vec = - Folder::find_by_user(&headers.user.uuid, &mut conn).await.into_iter().map(|f| f.uuid).collect(); + let existing_folders: HashSet> = + Folder::find_by_user(&headers.user.uuid, &mut conn).await.into_iter().map(|f| Some(f.uuid)).collect(); let mut folders: Vec = Vec::with_capacity(data.folders.len()); for folder in data.folders.into_iter() { - let folder_uuid = if folder.id.is_some() && existing_folders.contains(folder.id.as_ref().unwrap()) { + let folder_uuid = if existing_folders.contains(&folder.id) { folder.id.unwrap() } else { let mut new_folder = Folder::new(headers.user.uuid.clone(), folder.name); @@ -601,8 +601,8 @@ async fn post_ciphers_import( } // Read the relations between folders and ciphers + // Ciphers can only be in one folder at the same time let mut relations_map = HashMap::with_capacity(data.folder_relationships.len()); - for relation in data.folder_relationships { relations_map.insert(relation.key, relation.value); } diff --git a/src/api/core/organizations.rs b/src/api/core/organizations.rs index 7ee6a089..2b51a144 100644 --- a/src/api/core/organizations.rs +++ b/src/api/core/organizations.rs @@ -11,7 +11,6 @@ use crate::{ }, auth::{decode_invite, AdminHeaders, ClientVersion, Headers, ManagerHeaders, ManagerHeadersLoose, OwnerHeaders}, db::{models::*, DbConn}, - error::Error, mail, util::{convert_json_key_lcase_first, NumberOrString}, CONFIG, @@ -127,6 +126,7 @@ struct NewCollectionData { name: String, groups: Vec, users: Vec, + id: Option, external_id: Option, } @@ -1598,40 +1598,43 @@ async fn post_org_import( // TODO: See if we can optimize the whole cipher adding/importing and prevent duplicate code and checks. Cipher::validate_cipher_data(&data.ciphers)?; - let mut collections = Vec::new(); + let existing_collections: HashSet> = + Collection::find_by_organization(&org_id, &mut conn).await.into_iter().map(|c| (Some(c.uuid))).collect(); + let mut collections: Vec = Vec::with_capacity(data.collections.len()); for coll in data.collections { - let collection = Collection::new(org_id.clone(), coll.name, coll.external_id); - if collection.save(&mut conn).await.is_err() { - collections.push(Err(Error::new("Failed to create Collection", "Failed to create Collection"))); + let collection_uuid = if existing_collections.contains(&coll.id) { + coll.id.unwrap() } else { - collections.push(Ok(collection)); - } + let new_collection = Collection::new(org_id.clone(), coll.name, coll.external_id); + new_collection.save(&mut conn).await?; + new_collection.uuid + }; + + collections.push(collection_uuid); } // Read the relations between collections and ciphers - let mut relations = Vec::new(); + // Ciphers can be in multiple collections at the same time + let mut relations = Vec::with_capacity(data.collection_relationships.len()); for relation in data.collection_relationships { relations.push((relation.key, relation.value)); } let headers: Headers = headers.into(); - let mut ciphers = Vec::new(); - for cipher_data in data.ciphers { + let mut ciphers: Vec = Vec::with_capacity(data.ciphers.len()); + for mut cipher_data in data.ciphers { + // Always clear folder_id's via an organization import + cipher_data.folder_id = None; let mut cipher = Cipher::new(cipher_data.r#type, cipher_data.name.clone()); update_cipher_from_data(&mut cipher, cipher_data, &headers, None, &mut conn, &nt, UpdateType::None).await.ok(); - ciphers.push(cipher); + ciphers.push(cipher.uuid); } // Assign the collections for (cipher_index, coll_index) in relations { - let cipher_id = &ciphers[cipher_index].uuid; - let coll = &collections[coll_index]; - let coll_id = match coll { - Ok(coll) => coll.uuid.as_str(), - Err(_) => err!("Failed to assign to collection"), - }; - + let cipher_id = &ciphers[cipher_index]; + let coll_id = &collections[coll_index]; CollectionCipher::save(cipher_id, coll_id, &mut conn).await?; } diff --git a/src/api/core/two_factor/duo_oidc.rs b/src/api/core/two_factor/duo_oidc.rs index d252df91..eb7fb329 100644 --- a/src/api/core/two_factor/duo_oidc.rs +++ b/src/api/core/two_factor/duo_oidc.rs @@ -211,10 +211,7 @@ impl DuoClient { nonce, }; - let token = match self.encode_duo_jwt(jwt_payload) { - Ok(token) => token, - Err(e) => return Err(e), - }; + let token = self.encode_duo_jwt(jwt_payload)?; let authz_endpoint = format!("https://{}/oauth/v1/authorize", self.api_host); let mut auth_url = match Url::parse(authz_endpoint.as_str()) { From b0b953f348a30be02999d5a47cc2aa49b07813f6 Mon Sep 17 00:00:00 2001 From: Mathijs van Veluw Date: Wed, 20 Nov 2024 17:32:44 +0100 Subject: [PATCH 014/286] Fix push not working (#5214) The new native mobile clients seem to use PascalCase for the push payload. Also the date/time could cause issues. This PR fixes this by formatting the date/time correctly and use PascalCase for the payload key's I now receive cipher updates and login-with-device requests again. Fixes #5182 Signed-off-by: BlackDex --- src/api/push.rs | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/src/api/push.rs b/src/api/push.rs index eaf304f9..d6abfc49 100644 --- a/src/api/push.rs +++ b/src/api/push.rs @@ -9,6 +9,7 @@ use crate::{ api::{ApiResult, EmptyResult, UpdateType}, db::models::{Cipher, Device, Folder, Send, User}, http_client::make_http_request, + util::format_date, CONFIG, }; @@ -170,10 +171,10 @@ pub async fn push_cipher_update( "identifier": acting_device_uuid, "type": ut as i32, "payload": { - "id": cipher.uuid, - "userId": cipher.user_uuid, - "organizationId": (), - "revisionDate": cipher.updated_at + "Id": cipher.uuid, + "UserId": cipher.user_uuid, + "OrganizationId": (), + "RevisionDate": format_date(&cipher.updated_at) } })) .await; @@ -190,8 +191,8 @@ pub fn push_logout(user: &User, acting_device_uuid: Option) { "identifier": acting_device_uuid, "type": UpdateType::LogOut as i32, "payload": { - "userId": user.uuid, - "date": user.updated_at + "UserId": user.uuid, + "Date": format_date(&user.updated_at) } }))); } @@ -204,8 +205,8 @@ pub fn push_user_update(ut: UpdateType, user: &User) { "identifier": (), "type": ut as i32, "payload": { - "userId": user.uuid, - "date": user.updated_at + "UserId": user.uuid, + "Date": format_date(&user.updated_at) } }))); } @@ -224,9 +225,9 @@ pub async fn push_folder_update( "identifier": acting_device_uuid, "type": ut as i32, "payload": { - "id": folder.uuid, - "userId": folder.user_uuid, - "revisionDate": folder.updated_at + "Id": folder.uuid, + "UserId": folder.user_uuid, + "RevisionDate": format_date(&folder.updated_at) } }))); } @@ -242,9 +243,9 @@ pub async fn push_send_update(ut: UpdateType, send: &Send, acting_device_uuid: & "identifier": acting_device_uuid, "type": ut as i32, "payload": { - "id": send.uuid, - "userId": send.user_uuid, - "revisionDate": send.revision_date + "Id": send.uuid, + "UserId": send.user_uuid, + "RevisionDate": format_date(&send.revision_date) } }))); } @@ -295,8 +296,8 @@ pub async fn push_auth_request(user_uuid: String, auth_request_uuid: String, con "identifier": null, "type": UpdateType::AuthRequest as i32, "payload": { - "id": auth_request_uuid, - "userId": user_uuid, + "Id": auth_request_uuid, + "UserId": user_uuid, } }))); } @@ -316,8 +317,8 @@ pub async fn push_auth_response( "identifier": approving_device_uuid, "type": UpdateType::AuthRequestResponse as i32, "payload": { - "id": auth_request_uuid, - "userId": user_uuid, + "Id": auth_request_uuid, + "UserId": user_uuid, } }))); } From 96813b1317122e2e24d42dcad6c08e42548db9e4 Mon Sep 17 00:00:00 2001 From: Mathijs van Veluw Date: Wed, 20 Nov 2024 17:38:16 +0100 Subject: [PATCH 015/286] Fix editing members which have access-all rights (#5213) With web-vault v2024.6.2 and lower, if a user has access-all rights either as an org-member or via a group it shouldn't return individual collections. This probably needs to be changed with newer versions which do not support the `access-all` feature anymore and work with manage. But with the current version this should solve access right issues. Fixes #5212 Signed-off-by: BlackDex --- src/db/models/organization.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/db/models/organization.rs b/src/db/models/organization.rs index 5426fff0..15f00991 100644 --- a/src/db/models/organization.rs +++ b/src/db/models/organization.rs @@ -462,7 +462,13 @@ impl UserOrganization { Vec::with_capacity(0) }; - let collections: Vec = if include_collections { + // Check if a user is in a group which has access to all collections + // If that is the case, we should not return individual collections! + let full_access_group = + CONFIG.org_groups_enabled() && Group::is_in_full_access_group(&self.user_uuid, &self.org_uuid, conn).await; + + // If collections are to be included, only include them if the user does not have full access via a group or defined to the user it self + let collections: Vec = if include_collections && !(full_access_group || self.has_full_access()) { // Get all collections for the user here already to prevent more queries let cu: HashMap = CollectionUser::find_by_organization_and_user_uuid(&self.org_uuid, &self.user_uuid, conn) From da3701c0cfa5fb0fe505c18c5f210edb2a71aaf9 Mon Sep 17 00:00:00 2001 From: chuangjinglu Date: Tue, 26 Nov 2024 01:35:00 +0800 Subject: [PATCH 016/286] chore: fix some comments (#5224) Signed-off-by: chuangjinglu --- SECURITY.md | 2 +- docker/README.md | 2 +- docker/docker-bake.hcl | 2 +- src/api/core/organizations.rs | 4 ++-- src/api/icons.rs | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/SECURITY.md b/SECURITY.md index 0917981c..4d23e51c 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -21,7 +21,7 @@ notify us. We welcome working with you to resolve the issue promptly. Thanks in The following bug classes are out-of scope: - Bugs that are already reported on Vaultwarden's issue tracker (https://github.com/dani-garcia/vaultwarden/issues) -- Bugs that are not part of Vaultwarden, like on the the web-vault or mobile and desktop clients. These issues need to be reported in the respective project issue tracker at https://github.com/bitwarden to which we are not associated +- Bugs that are not part of Vaultwarden, like on the web-vault or mobile and desktop clients. These issues need to be reported in the respective project issue tracker at https://github.com/bitwarden to which we are not associated - Issues in an upstream software dependency (ex: Rust, or External Libraries) which are already reported to the upstream maintainer - Attacks requiring physical access to a user's device - Issues related to software or protocols not under Vaultwarden's control diff --git a/docker/README.md b/docker/README.md index 2e78f534..f76cd35d 100644 --- a/docker/README.md +++ b/docker/README.md @@ -46,7 +46,7 @@ There also is an option to use an other docker container to provide support for ```bash # To install and activate docker run --privileged --rm tonistiigi/binfmt --install arm64,arm -# To unistall +# To uninstall docker run --privileged --rm tonistiigi/binfmt --uninstall 'qemu-*' ``` diff --git a/docker/docker-bake.hcl b/docker/docker-bake.hcl index 38e7ef97..2edf4fbb 100644 --- a/docker/docker-bake.hcl +++ b/docker/docker-bake.hcl @@ -17,7 +17,7 @@ variable "SOURCE_REPOSITORY_URL" { default = null } -// The commit hash of of the current commit this build was triggered on +// The commit hash of the current commit this build was triggered on variable "SOURCE_COMMIT" { default = null } diff --git a/src/api/core/organizations.rs b/src/api/core/organizations.rs index 2b51a144..2bff64b8 100644 --- a/src/api/core/organizations.rs +++ b/src/api/core/organizations.rs @@ -1652,7 +1652,7 @@ struct BulkCollectionsData { remove_collections: bool, } -// This endpoint is only reachable via the organization view, therefor this endpoint is located here +// This endpoint is only reachable via the organization view, therefore this endpoint is located here // Also Bitwarden does not send out Notifications for these changes, it only does this for individual cipher collection updates #[post("/ciphers/bulk-collections", data = "")] async fn post_bulk_collections(data: Json, headers: Headers, mut conn: DbConn) -> EmptyResult { @@ -2789,7 +2789,7 @@ struct OrganizationUserResetPasswordRequest { key: String, } -// Upstrem reports this is the renamed endpoint instead of `/keys` +// Upstream reports this is the renamed endpoint instead of `/keys` // But the clients do not seem to use this at all // Just add it here in case they will #[get("/organizations//public-key")] diff --git a/src/api/icons.rs b/src/api/icons.rs index 6afbaa9f..ebcace6d 100644 --- a/src/api/icons.rs +++ b/src/api/icons.rs @@ -662,7 +662,7 @@ impl reqwest::cookie::CookieStore for Jar { /// The FaviconEmitter is using an optimized version of the DefaultEmitter. /// This prevents emitting tags like comments, doctype and also strings between the tags. /// But it will also only emit the tags we need and only if they have the correct attributes -/// Therefor parsing the HTML content is faster. +/// Therefore parsing the HTML content is faster. use std::collections::BTreeMap; #[derive(Default)] From 71b3d3c818db846bb1972ed79188b3709b9f822b Mon Sep 17 00:00:00 2001 From: Mathijs van Veluw Date: Thu, 5 Dec 2024 22:10:59 +0100 Subject: [PATCH 017/286] Update Rust and crates (#5248) * Update Rust and crates - Updated Rust to v1.83.0 - Updated MSRV to v1.82.0 (Needed for html5gum crate) - Updated icon fetching code to match new html5gum version - Updated workflows - Enabled edition 2024 clippy lints Nightly reports some clippy hints, but that would be too much to change in this PR i think. Signed-off-by: BlackDex * Some additional updates - Patch fern to allow syslog-7 feature - Fixed diesel logger which was broken because of the sqlite backup feature Refactored the sqlite backup because of this - Added a build workflow test to include the query_logger feature Signed-off-by: BlackDex * Also patch yubico-rs and latest updates Signed-off-by: BlackDex --------- Signed-off-by: BlackDex --- .github/workflows/build.yml | 15 +- .github/workflows/hadolint.yml | 2 +- .github/workflows/release.yml | 2 +- .github/workflows/trivy.yml | 9 +- Cargo.lock | 456 ++++++++++++--------------------- Cargo.toml | 40 +-- docker/DockerSettings.yaml | 2 +- docker/Dockerfile.alpine | 8 +- docker/Dockerfile.debian | 2 +- rust-toolchain.toml | 2 +- src/api/icons.rs | 12 +- src/db/mod.rs | 22 +- src/main.rs | 39 ++- 13 files changed, 242 insertions(+), 369 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a025041f..7da6b91b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -47,7 +47,7 @@ jobs: steps: # Checkout the repo - name: "Checkout" - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 #v4.2.1 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.2.2 # End Checkout the repo @@ -75,7 +75,7 @@ jobs: # Only install the clippy and rustfmt components on the default rust-toolchain - name: "Install rust-toolchain version" - uses: dtolnay/rust-toolchain@7b1c307e0dcbda6122208f10795a713336a9b35a # master @ Aug 8, 2024, 7:36 PM GMT+2 + uses: dtolnay/rust-toolchain@315e265cd78dad1e1dcf3a5074f6d6c47029d5aa # master @ Nov 18, 2024, 5:36 AM GMT+1 if: ${{ matrix.channel == 'rust-toolchain' }} with: toolchain: "${{steps.toolchain.outputs.RUST_TOOLCHAIN}}" @@ -85,7 +85,7 @@ jobs: # Install the any other channel to be used for which we do not execute clippy and rustfmt - name: "Install MSRV version" - uses: dtolnay/rust-toolchain@7b1c307e0dcbda6122208f10795a713336a9b35a # master @ Aug 8, 2024, 7:36 PM GMT+2 + uses: dtolnay/rust-toolchain@315e265cd78dad1e1dcf3a5074f6d6c47029d5aa # master @ Nov 18, 2024, 5:36 AM GMT+1 if: ${{ matrix.channel != 'rust-toolchain' }} with: toolchain: "${{steps.toolchain.outputs.RUST_TOOLCHAIN}}" @@ -107,7 +107,7 @@ jobs: # End Show environment # Enable Rust Caching - - uses: Swatinem/rust-cache@23bce251a8cd2ffc3c1075eaa2367cf899916d84 # v2.7.3 + - uses: Swatinem/rust-cache@82a92a6e8fbeee089604da2575dc567ae9ddeaab # v2.7.5 with: # Use a custom prefix-key to force a fresh start. This is sometimes needed with bigger changes. # Like changing the build host from Ubuntu 20.04 to 22.04 for example. @@ -117,6 +117,12 @@ jobs: # Run cargo tests # First test all features together, afterwards test them separately. + - name: "test features: sqlite,mysql,postgresql,enable_mimalloc,query_logger" + id: test_sqlite_mysql_postgresql_mimalloc_logger + if: $${{ always() }} + run: | + cargo test --features sqlite,mysql,postgresql,enable_mimalloc,query_logger + - name: "test features: sqlite,mysql,postgresql,enable_mimalloc" id: test_sqlite_mysql_postgresql_mimalloc if: $${{ always() }} @@ -176,6 +182,7 @@ jobs: echo "" >> $GITHUB_STEP_SUMMARY echo "|Job|Status|" >> $GITHUB_STEP_SUMMARY echo "|---|------|" >> $GITHUB_STEP_SUMMARY + echo "|test (sqlite,mysql,postgresql,enable_mimalloc,query_logger)|${{ steps.test_sqlite_mysql_postgresql_mimalloc_logger.outcome }}|" >> $GITHUB_STEP_SUMMARY echo "|test (sqlite,mysql,postgresql,enable_mimalloc)|${{ steps.test_sqlite_mysql_postgresql_mimalloc.outcome }}|" >> $GITHUB_STEP_SUMMARY echo "|test (sqlite,mysql,postgresql)|${{ steps.test_sqlite_mysql_postgresql.outcome }}|" >> $GITHUB_STEP_SUMMARY echo "|test (sqlite)|${{ steps.test_sqlite.outcome }}|" >> $GITHUB_STEP_SUMMARY diff --git a/.github/workflows/hadolint.yml b/.github/workflows/hadolint.yml index a671f936..35bb3432 100644 --- a/.github/workflows/hadolint.yml +++ b/.github/workflows/hadolint.yml @@ -13,7 +13,7 @@ jobs: steps: # Checkout the repo - name: Checkout - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 #v4.2.1 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.2.2 # End Checkout the repo # Start Docker Buildx diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 22fc4e28..93b8919d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -58,7 +58,7 @@ jobs: steps: # Checkout the repo - name: Checkout - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 #v4.2.1 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.2.2 with: fetch-depth: 0 diff --git a/.github/workflows/trivy.yml b/.github/workflows/trivy.yml index 48a8bc1e..4481ec6a 100644 --- a/.github/workflows/trivy.yml +++ b/.github/workflows/trivy.yml @@ -28,10 +28,13 @@ jobs: actions: read steps: - name: Checkout code - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 #v4.2.1 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.2.2 - name: Run Trivy vulnerability scanner - uses: aquasecurity/trivy-action@5681af892cd0f4997658e2bacc62bd0a894cf564 # v0.27.0 + uses: aquasecurity/trivy-action@18f2510ee396bbf400402947b394f2dd8c87dbb0 # v0.29.0 + env: + TRIVY_DB_REPOSITORY: docker.io/aquasec/trivy-db:2,public.ecr.aws/aquasecurity/trivy-db:2,ghcr.io/aquasecurity/trivy-db:2 + TRIVY_JAVA_DB_REPOSITORY: docker.io/aquasec/trivy-java-db:1,public.ecr.aws/aquasecurity/trivy-java-db:1,ghcr.io/aquasecurity/trivy-java-db:1 with: scan-type: repo ignore-unfixed: true @@ -40,6 +43,6 @@ jobs: severity: CRITICAL,HIGH - name: Upload Trivy scan results to GitHub Security tab - uses: github/codeql-action/upload-sarif@2bbafcdd7fbf96243689e764c2f15d9735164f33 # v3.26.6 + uses: github/codeql-action/upload-sarif@86b04fb0e47484f7282357688f21d5d0e32175fe # v3.27.5 with: sarif_file: 'trivy-results.sarif' diff --git a/Cargo.lock b/Cargo.lock index 9edd20bf..497a38ac 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -55,9 +55,9 @@ dependencies = [ [[package]] name = "allocator-api2" -version = "0.2.19" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "611cc2ae7d2e242c457e4be7f97036b8ad9ca152b499f53faf99b1ed8fc2553f" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "android-tzdata" @@ -111,9 +111,9 @@ dependencies = [ [[package]] name = "async-compression" -version = "0.4.17" +version = "0.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cb8f1d480b0ea3783ab015936d2a55c87e219676f0c0b7dec61494043f21857" +checksum = "df895a515f70646414f4b45c0b79082783b80552b373a68283012928df56f522" dependencies = [ "brotli", "flate2", @@ -369,12 +369,6 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "383d29d513d8764dcdc42ea295d979eb99c3c9f00607b3692cf68a431f7dca72" -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - [[package]] name = "bitflags" version = "2.6.0" @@ -441,9 +435,9 @@ checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "bytemuck" -version = "1.19.0" +version = "1.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8334215b81e418a0a7bdb8ef0849474f40bb10c8b71f1c4ed315cff49f32494d" +checksum = "8b37c88a63ffd85d15b406896cc343916d7cf57838a847b3a6f2ca5d39a5695a" [[package]] name = "byteorder" @@ -453,9 +447,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da" +checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" [[package]] name = "cached" @@ -495,9 +489,9 @@ checksum = "ade8366b8bd5ba243f0a58f036cc0ca8a2f069cff1a2351ef1cac6b083e16fc0" [[package]] name = "cc" -version = "1.1.37" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40545c26d092346d8a8dab71ee48e7685a7a9cba76e634790c215b41a4a7b4cf" +checksum = "f34d93e62b03caf570cccc334cbc6c2fceca82f39211051345108adcba3eebdc" dependencies = [ "shlex", ] @@ -614,9 +608,9 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "cpufeatures" -version = "0.2.14" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" +checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3" dependencies = [ "libc", ] @@ -753,7 +747,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b035a542cf7abf01f2e3c4d5a7acbaebfefe120ae4efc7bde3df98186e4b8af7" dependencies = [ - "bitflags 2.6.0", + "bitflags", "proc-macro2", "proc-macro2-diagnostics", "quote", @@ -762,12 +756,12 @@ dependencies = [ [[package]] name = "diesel" -version = "2.2.4" +version = "2.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "158fe8e2e68695bd615d7e4f3227c0727b151330d3e253b525086c348d055d5e" +checksum = "ccf1bedf64cdb9643204a36dd15b19a6ce8e7aa7f7b105868e9f1fad5ffa7d12" dependencies = [ "bigdecimal", - "bitflags 2.6.0", + "bitflags", "byteorder", "chrono", "diesel_derives", @@ -799,9 +793,9 @@ dependencies = [ [[package]] name = "diesel_logger" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23010b507517129dc9b11fb35f36d76fd2d3dd4c85232733697622e345375f2f" +checksum = "8074833fffb675cf22a6ee669124f65f02971e48dd520bb80c7473ff70aeaf95" dependencies = [ "diesel", "log", @@ -886,9 +880,9 @@ checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] name = "email-encoding" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60d1d33cdaede7e24091f039632eb5d3c7469fe5b066a985281a34fc70fa317f" +checksum = "ea3d894bbbab314476b265f9b2d46bf24b123a36dd0e96b06a1b49545b9d9dcc" dependencies = [ "base64 0.22.1", "memchr", @@ -932,21 +926,12 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" dependencies = [ "libc", - "windows-sys 0.52.0", -] - -[[package]] -name = "error-chain" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc" -dependencies = [ - "version_check", + "windows-sys 0.59.0", ] [[package]] @@ -968,9 +953,9 @@ dependencies = [ [[package]] name = "event-listener-strategy" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" +checksum = "3c3e4e0dd3673c1139bf041f3008816d9cf2946bbfac2945c09e523b8d7b05b2" dependencies = [ "event-listener 5.3.1", "pin-project-lite", @@ -985,8 +970,7 @@ checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4" [[package]] name = "fern" version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69ff9c9d5fb3e6da8ac2f77ab76fe7e8087d512ce095200f8f29ac5b656cf6dc" +source = "git+https://github.com/daboross/fern?rev=3e775ccfafe7d24baee39826d38011981b2e55b5#3e775ccfafe7d24baee39826d38011981b2e55b5" dependencies = [ "libc", "log", @@ -1010,9 +994,9 @@ dependencies = [ [[package]] name = "flate2" -version = "1.0.34" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1b589b4dc103969ad3cf85c950899926ec64300a1a46d76c03a6072957036f0" +checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" dependencies = [ "crc32fast", "miniz_oxide", @@ -1252,35 +1236,16 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.26" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" -dependencies = [ - "bytes", - "fnv", - "futures-core", - "futures-sink", - "futures-util", - "http 0.2.12", - "indexmap", - "slab", - "tokio", - "tokio-util", - "tracing", -] - -[[package]] -name = "h2" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e8ac6999421f49a846c2d4411f337e53497d8ec55d67753beffa43c5d9205" +checksum = "ccae279728d634d083c00f6099cb58f01cc99c145b84b8be2f6c74618d79922e" dependencies = [ "atomic-waker", "bytes", "fnv", "futures-core", "futures-sink", - "http 1.1.0", + "http 1.2.0", "indexmap", "slab", "tokio", @@ -1322,9 +1287,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.1" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" [[package]] name = "heck" @@ -1431,9 +1396,9 @@ dependencies = [ [[package]] name = "html5gum" -version = "0.6.1" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91b361633dcc40096d01de35ed535b6089be91880be47b6fd8f560497af7f716" +checksum = "b3918b5f36d61861b757261da986b51be562c7a87ac4e531d4158e67e08bff72" dependencies = [ "jetscii", ] @@ -1451,9 +1416,9 @@ dependencies = [ [[package]] name = "http" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea" dependencies = [ "bytes", "fnv", @@ -1478,7 +1443,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "http 1.1.0", + "http 1.2.0", ] [[package]] @@ -1489,7 +1454,7 @@ checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" dependencies = [ "bytes", "futures-util", - "http 1.1.0", + "http 1.2.0", "http-body 1.0.1", "pin-project-lite", ] @@ -1516,7 +1481,6 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", - "h2 0.3.26", "http 0.2.12", "http-body 0.4.6", "httparse", @@ -1532,15 +1496,15 @@ dependencies = [ [[package]] name = "hyper" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbbff0a806a4728c99295b254c8838933b5b082d75e3cb70c8dab21fdfbcfa9a" +checksum = "97818827ef4f364230e16705d4706e2897df2bb60617d6ca15d598025a3c481f" dependencies = [ "bytes", "futures-channel", "futures-util", - "h2 0.4.6", - "http 1.1.0", + "h2", + "http 1.2.0", "http-body 1.0.1", "httparse", "itoa", @@ -1557,29 +1521,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" dependencies = [ "futures-util", - "http 1.1.0", - "hyper 1.5.0", + "http 1.2.0", + "hyper 1.5.1", "hyper-util", - "rustls 0.23.16", + "rustls 0.23.19", "rustls-pki-types", "tokio", "tokio-rustls 0.26.0", "tower-service", ] -[[package]] -name = "hyper-tls" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" -dependencies = [ - "bytes", - "hyper 0.14.31", - "native-tls", - "tokio", - "tokio-native-tls", -] - [[package]] name = "hyper-tls" version = "0.6.0" @@ -1588,7 +1539,7 @@ checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ "bytes", "http-body-util", - "hyper 1.5.0", + "hyper 1.5.1", "hyper-util", "native-tls", "tokio", @@ -1605,9 +1556,9 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "http 1.1.0", + "http 1.2.0", "http-body 1.0.1", - "hyper 1.5.0", + "hyper 1.5.1", "pin-project-lite", "socket2", "tokio", @@ -1762,16 +1713,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" -[[package]] -name = "idna" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - [[package]] name = "idna" version = "0.4.0" @@ -1805,12 +1746,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" +checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" dependencies = [ "equivalent", - "hashbrown 0.15.1", + "hashbrown 0.15.2", "serde", ] @@ -1851,9 +1792,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.11" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" [[package]] name = "jetscii" @@ -1874,10 +1815,11 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.72" +version = "0.3.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" +checksum = "a865e038f7f6ed956f788f0d7d60c541fff74c7bd74272c5d4cf15c63743e705" dependencies = [ + "once_cell", "wasm-bindgen", ] @@ -1953,9 +1895,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.162" +version = "0.2.167" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398" +checksum = "09d6582e104315a817dff97f75133544b2e094ee22447d2acf4a74e189ba06fc" [[package]] name = "libm" @@ -1998,9 +1940,9 @@ checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] name = "litemap" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "643cb0b8d4fcc284004d5fd0d67ccf61dfffadb7f75e1e71bc420f4688a3a704" +checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" [[package]] name = "litrs" @@ -2125,11 +2067,10 @@ dependencies = [ [[package]] name = "mio" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" +checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" dependencies = [ - "hermit-abi 0.3.9", "libc", "wasi", "windows-sys 0.52.0", @@ -2144,7 +2085,7 @@ dependencies = [ "bytes", "encoding_rs", "futures-util", - "http 1.1.0", + "http 1.2.0", "httparse", "memchr", "mime", @@ -2156,9 +2097,9 @@ dependencies = [ [[package]] name = "mysqlclient-sys" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "478e2040dbc35c73927b77a2be91a496de19deab376a6982ed61e89592434619" +checksum = "6bbb9b017b98c4cde5802998113e182eecc1ebce8d47e9ea1697b9a623d53870" dependencies = [ "pkg-config", "vcpkg", @@ -2313,7 +2254,7 @@ version = "0.10.68" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6174bc48f102d208783c2c84bf931bb75927a617866870de8a4ea85597f871f5" dependencies = [ - "bitflags 2.6.0", + "bitflags", "cfg-if", "foreign-types", "libc", @@ -2341,9 +2282,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-src" -version = "300.4.0+3.4.0" +version = "300.4.1+3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a709e02f2b4aca747929cca5ed248880847c650233cf8b8cdc48f40aaf4898a6" +checksum = "faa4eac4138c62414b5622d1b31c5c304f34b406b013c079c2bbc652fdd6678c" dependencies = [ "cc", ] @@ -2610,9 +2551,9 @@ dependencies = [ [[package]] name = "portable-atomic" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc9c68a3f6da06753e9335d63e27f6b9754dd1920d941135b7ea8224f141adb2" +checksum = "280dc24453071f1b63954171985a0b0d30058d287960968b9b2aca264c8d4ee6" [[package]] name = "powerfmt" @@ -2640,9 +2581,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.89" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" dependencies = [ "unicode-ident", ] @@ -2668,20 +2609,20 @@ checksum = "33cb294fe86a74cbcf50d4445b37da762029549ebeea341421c7c70370f86cac" [[package]] name = "psm" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa37f80ca58604976033fae9515a8a2989fc13797d953f7c04fb8fa36a11f205" +checksum = "200b9ff220857e53e184257720a14553b2f4aa02577d2ed9842d45d4b9654810" dependencies = [ "cc", ] [[package]] name = "publicsuffix" -version = "2.2.3" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96a8c1bda5ae1af7f99a2962e49df150414a43d62404644d98dd5c3a93d07457" +checksum = "6f42ea446cab60335f76979ec15e12619a2165b5ae2c12166bef27d283a9fadf" dependencies = [ - "idna 0.3.0", + "idna 1.0.3", "psl-types", ] @@ -2768,7 +2709,7 @@ version = "11.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ab240315c661615f2ee9f0f2cd32d5a7343a84d5ebcccb99d46e6637565e7b0" dependencies = [ - "bitflags 2.6.0", + "bitflags", ] [[package]] @@ -2777,7 +2718,7 @@ version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" dependencies = [ - "bitflags 2.6.0", + "bitflags", ] [[package]] @@ -2808,7 +2749,7 @@ checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.8", + "regex-automata 0.4.9", "regex-syntax 0.8.5", ] @@ -2823,9 +2764,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", @@ -2855,46 +2796,6 @@ dependencies = [ "signal-hook", ] -[[package]] -name = "reqwest" -version = "0.11.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" -dependencies = [ - "base64 0.21.7", - "bytes", - "encoding_rs", - "futures-core", - "futures-util", - "h2 0.3.26", - "http 0.2.12", - "http-body 0.4.6", - "hyper 0.14.31", - "hyper-tls 0.5.0", - "ipnet", - "js-sys", - "log", - "mime", - "native-tls", - "once_cell", - "percent-encoding", - "pin-project-lite", - "rustls-pemfile 1.0.4", - "serde", - "serde_json", - "serde_urlencoded", - "sync_wrapper 0.1.2", - "system-configuration 0.5.1", - "tokio", - "tokio-native-tls", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "winreg", -] - [[package]] name = "reqwest" version = "0.12.9" @@ -2907,15 +2808,16 @@ dependencies = [ "cookie", "cookie_store", "encoding_rs", + "futures-channel", "futures-core", "futures-util", - "h2 0.4.6", - "http 1.1.0", + "h2", + "http 1.2.0", "http-body 1.0.1", "http-body-util", - "hyper 1.5.0", + "hyper 1.5.1", "hyper-rustls", - "hyper-tls 0.6.0", + "hyper-tls", "hyper-util", "ipnet", "js-sys", @@ -2929,8 +2831,8 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", - "sync_wrapper 1.0.1", - "system-configuration 0.6.1", + "sync_wrapper", + "system-configuration", "tokio", "tokio-native-tls", "tokio-socks", @@ -3114,11 +3016,11 @@ checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustix" -version = "0.38.40" +version = "0.38.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99e4ea3e1cdc4b559b8e5650f9c8e5998e3e5c1343b4eaf034565f32318d63c0" +checksum = "d7f649912bc1495e167a6edee79151c84b1bad49748cb4f1f1167f459f6224f6" dependencies = [ - "bitflags 2.6.0", + "bitflags", "errno", "libc", "linux-raw-sys", @@ -3139,9 +3041,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.16" +version = "0.23.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eee87ff5d9b36712a58574e12e9f0ea80f915a5b0ac518d322b24a465617925e" +checksum = "934b404430bb06b3fae2cba809eb45a1ab1aecd64491213d7c3301b88393f8d1" dependencies = [ "once_cell", "rustls-pki-types", @@ -3218,9 +3120,9 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.26" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01227be5826fa0690321a2ba6c5cd57a19cf3f6a09e76973b58e61de6ab9d1c1" +checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" dependencies = [ "windows-sys 0.59.0", ] @@ -3262,7 +3164,7 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.6.0", + "bitflags", "core-foundation", "core-foundation-sys", "libc", @@ -3287,9 +3189,9 @@ checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" [[package]] name = "serde" -version = "1.0.214" +version = "1.0.215" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5" +checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" dependencies = [ "serde_derive", ] @@ -3306,9 +3208,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.214" +version = "1.0.215" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766" +checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" dependencies = [ "proc-macro2", "quote", @@ -3317,9 +3219,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.132" +version = "1.0.133" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" +checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" dependencies = [ "itoa", "memchr", @@ -3439,9 +3341,9 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "socket2" -version = "0.5.7" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" dependencies = [ "libc", "windows-sys 0.52.0", @@ -3513,9 +3415,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.87" +version = "2.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" +checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" dependencies = [ "proc-macro2", "quote", @@ -3524,15 +3426,9 @@ dependencies = [ [[package]] name = "sync_wrapper" -version = "0.1.2" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" - -[[package]] -name = "sync_wrapper" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" dependencies = [ "futures-core", ] @@ -3550,47 +3446,25 @@ dependencies = [ [[package]] name = "syslog" -version = "6.1.1" +version = "7.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfc7e95b5b795122fafe6519e27629b5ab4232c73ebb2428f568e82b1a457ad3" +checksum = "019f1500a13379b7d051455df397c75770de6311a7a188a699499502704d9f10" dependencies = [ - "error-chain", - "hostname 0.3.1", + "hostname 0.4.0", "libc", "log", "time", ] -[[package]] -name = "system-configuration" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" -dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "system-configuration-sys 0.5.0", -] - [[package]] name = "system-configuration" version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ - "bitflags 2.6.0", + "bitflags", "core-foundation", - "system-configuration-sys 0.6.0", -] - -[[package]] -name = "system-configuration-sys" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" -dependencies = [ - "core-foundation-sys", - "libc", + "system-configuration-sys", ] [[package]] @@ -3657,9 +3531,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.36" +version = "0.3.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21" dependencies = [ "deranged", "itoa", @@ -3680,9 +3554,9 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.18" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de" dependencies = [ "num-conv", "time-core", @@ -3715,9 +3589,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.41.1" +version = "1.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cfb5bee7a6a52939ca9224d6ac897bb669134078daa8735560897f69de4d33" +checksum = "5cec9b21b0450273377fc97bd4c33a8acffc8c996c987a7c5b319a0083707551" dependencies = [ "backtrace", "bytes", @@ -3768,7 +3642,7 @@ version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" dependencies = [ - "rustls 0.23.16", + "rustls 0.23.19", "rustls-pki-types", "tokio", ] @@ -3810,9 +3684,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.12" +version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" +checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078" dependencies = [ "bytes", "futures-core", @@ -3875,9 +3749,9 @@ checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" -version = "0.1.40" +version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ "log", "pin-project-lite", @@ -3887,9 +3761,9 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.27" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", @@ -3898,9 +3772,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.32" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" dependencies = [ "once_cell", "valuable", @@ -3919,9 +3793,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.18" +version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" dependencies = [ "matchers", "nu-ansi-term", @@ -3950,7 +3824,7 @@ dependencies = [ "byteorder", "bytes", "data-encoding", - "http 1.1.0", + "http 1.2.0", "httparse", "log", "rand", @@ -3999,9 +3873,9 @@ checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893" [[package]] name = "unicode-ident" -version = "1.0.13" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" [[package]] name = "unicode-normalization" @@ -4026,9 +3900,9 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.5.3" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d157f1b96d14500ffdc1f10ba712e780825526c03d9a49b4d0324b0d9113ada" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" dependencies = [ "form_urlencoded", "idna 1.0.3", @@ -4117,7 +3991,7 @@ dependencies = [ "pico-args", "rand", "regex", - "reqwest 0.12.9", + "reqwest", "ring", "rmpv", "rocket", @@ -4177,9 +4051,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.95" +version = "0.2.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" +checksum = "d15e63b4482863c109d70a7b8706c1e364eb6ea449b201a76c5b89cedcec2d5c" dependencies = [ "cfg-if", "once_cell", @@ -4188,9 +4062,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.95" +version = "0.2.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" +checksum = "8d36ef12e3aaca16ddd3f67922bc63e48e953f126de60bd33ccc0101ef9998cd" dependencies = [ "bumpalo", "log", @@ -4203,21 +4077,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.45" +version = "0.4.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc7ec4f8827a71586374db3e87abdb5a2bb3a15afed140221307c3ec06b1f63b" +checksum = "9dfaf8f50e5f293737ee323940c7d8b08a66a95a419223d9f41610ca08b0833d" dependencies = [ "cfg-if", "js-sys", + "once_cell", "wasm-bindgen", "web-sys", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.95" +version = "0.2.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" +checksum = "705440e08b42d3e4b36de7d66c944be628d579796b8090bfa3471478a2260051" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -4225,9 +4100,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.95" +version = "0.2.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" +checksum = "98c9ae5a76e46f4deecd0f0255cc223cfa18dc9b261213b8aa0c7b36f61b3f1d" dependencies = [ "proc-macro2", "quote", @@ -4238,9 +4113,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.95" +version = "0.2.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" +checksum = "6ee99da9c5ba11bd675621338ef6fa52296b76b83305e9b6e5c77d4c286d6d49" [[package]] name = "wasm-streams" @@ -4257,9 +4132,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.72" +version = "0.3.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112" +checksum = "a98bc3c33f0fe7e59ad7cd041b89034fa82a7c2d4365ca538dda6cdaf513863c" dependencies = [ "js-sys", "wasm-bindgen", @@ -4597,9 +4472,9 @@ dependencies = [ [[package]] name = "yoke" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c5b1314b079b0930c31e3af543d8ee1757b1951ae1e1565ec704403a7240ca5" +checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" dependencies = [ "serde", "stable_deref_trait", @@ -4609,9 +4484,9 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95" +checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", @@ -4621,16 +4496,15 @@ dependencies = [ [[package]] name = "yubico" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "173f75d2c4010429a2d74ae3a114a69930c59e2b1a4c97b1c75d259a4960d5fb" +version = "0.12.0" +source = "git+https://github.com/BlackDex/yubico-rs?rev=00df14811f58155c0f02e3ab10f1570ed3e115c6#00df14811f58155c0f02e3ab10f1570ed3e115c6" dependencies = [ - "base64 0.13.1", + "base64 0.22.1", "form_urlencoded", "futures", "hmac", "rand", - "reqwest 0.11.27", + "reqwest", "sha1", "threadpool", ] @@ -4658,18 +4532,18 @@ dependencies = [ [[package]] name = "zerofrom" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ec111ce797d0e0784a1116d0ddcdbea84322cd79e5d5ad173daeba4f93ab55" +checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" dependencies = [ "zerofrom-derive", ] [[package]] name = "zerofrom-derive" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5" +checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 150b3b9d..cae715e6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ name = "vaultwarden" version = "1.0.0" authors = ["Daniel García "] edition = "2021" -rust-version = "1.80.0" +rust-version = "1.82.0" resolver = "2" repository = "https://github.com/dani-garcia/vaultwarden" @@ -36,13 +36,13 @@ unstable = [] [target."cfg(unix)".dependencies] # Logging -syslog = "6.1.1" +syslog = "7.0.0" [dependencies] # Logging log = "0.4.22" -fern = { version = "0.7.0", features = ["syslog-6", "reopen-1"] } -tracing = { version = "0.1.40", features = ["log"] } # Needed to have lettre and webauthn-rs trace logging to work +fern = { version = "0.7.0", features = ["syslog-7", "reopen-1"] } +tracing = { version = "0.1.41", features = ["log"] } # Needed to have lettre and webauthn-rs trace logging to work # A `dotenv` implementation for Rust dotenvy = { version = "0.15.7", default-features = false } @@ -67,16 +67,16 @@ dashmap = "6.1.0" # Async futures futures = "0.3.31" -tokio = { version = "1.41.1", features = ["rt-multi-thread", "fs", "io-util", "parking_lot", "time", "signal", "net"] } +tokio = { version = "1.42.0", features = ["rt-multi-thread", "fs", "io-util", "parking_lot", "time", "signal", "net"] } # A generic serialization/deserialization framework -serde = { version = "1.0.214", features = ["derive"] } -serde_json = "1.0.132" +serde = { version = "1.0.215", features = ["derive"] } +serde_json = "1.0.133" # A safe, extensible ORM and Query builder -diesel = { version = "2.2.4", features = ["chrono", "r2d2", "numeric"] } +diesel = { version = "2.2.6", features = ["chrono", "r2d2", "numeric"] } diesel_migrations = "2.2.0" -diesel_logger = { version = "0.3.0", optional = true } +diesel_logger = { version = "0.4.0", optional = true } # Bundled/Static SQLite libsqlite3-sys = { version = "0.30.1", features = ["bundled"], optional = true } @@ -91,7 +91,7 @@ uuid = { version = "1.11.0", features = ["v4"] } # Date and time libraries chrono = { version = "0.4.38", features = ["clock", "serde"], default-features = false } chrono-tz = "0.10.0" -time = "0.3.36" +time = "0.3.37" # Job scheduler job_scheduler_ng = "2.0.5" @@ -106,13 +106,13 @@ jsonwebtoken = "9.3.0" totp-lite = "2.0.1" # Yubico Library -yubico = { version = "0.11.0", features = ["online-tokio"], default-features = false } +yubico = { version = "0.12.0", features = ["online-tokio"], default-features = false } # WebAuthn libraries webauthn-rs = "0.3.2" # Handling of URL's for WebAuthn and favicons -url = "2.5.3" +url = "2.5.4" # Email libraries lettre = { version = "0.11.10", features = ["smtp-transport", "sendmail-transport", "builder", "serde", "tokio1-native-tls", "hostname", "tracing", "tokio1"], default-features = false } @@ -127,10 +127,10 @@ reqwest = { version = "0.12.9", features = ["native-tls-alpn", "stream", "json", hickory-resolver = "0.24.1" # Favicon extraction libraries -html5gum = "0.6.1" +html5gum = "0.7.0" regex = { version = "1.11.1", features = ["std", "perf", "unicode-perl"], default-features = false } data-url = "0.3.1" -bytes = "1.8.0" +bytes = "1.9.0" # Cache function results (Used for version check and favicon fetching) cached = { version = "0.54.0", features = ["async"] } @@ -166,6 +166,12 @@ rpassword = "7.3.1" # Loading a dynamic CSS Stylesheet grass_compiler = { version = "0.13.4", default-features = false } +[patch.crates-io] +# Patch fern to support syslog v7 +fern = { git = "https://github.com/daboross/fern", rev = "3e775ccfafe7d24baee39826d38011981b2e55b5" } +# Patch yubico to remove duplicate crates of older versions +yubico = { git = "https://github.com/BlackDex/yubico-rs", rev = "00df14811f58155c0f02e3ab10f1570ed3e115c6" } + # Strip debuginfo from the release builds # The symbols are the provide better panic traces # Also enable fat LTO and use 1 codegen unit for optimizations @@ -216,7 +222,8 @@ noop_method_call = "deny" refining_impl_trait = { level = "deny", priority = -1 } rust_2018_idioms = { level = "deny", priority = -1 } rust_2021_compatibility = { level = "deny", priority = -1 } -# rust_2024_compatibility = { level = "deny", priority = -1 } # Enable once we are at MSRV 1.81.0 +rust_2024_compatibility = { level = "deny", priority = -1 } +edition_2024_expr_fragment_specifier = "allow" # Once changed to Rust 2024 this should be removed and macro's should be validated again single_use_lifetimes = "deny" trivial_casts = "deny" trivial_numeric_casts = "deny" @@ -225,9 +232,6 @@ unused_import_braces = "deny" unused_lifetimes = "deny" unused_qualifications = "deny" variant_size_differences = "deny" -# The lints below are part of the rust_2024_compatibility group -static-mut-refs = "deny" -unsafe-op-in-unsafe-fn = "deny" # https://rust-lang.github.io/rust-clippy/stable/index.html [lints.clippy] diff --git a/docker/DockerSettings.yaml b/docker/DockerSettings.yaml index c4c541fb..dbfdbed3 100644 --- a/docker/DockerSettings.yaml +++ b/docker/DockerSettings.yaml @@ -5,7 +5,7 @@ vault_image_digest: "sha256:409ab328ca931439cb916b388a4bb784bd44220717aaf74cf716 # We use the linux/amd64 platform shell scripts since there is no difference between the different platform scripts # https://github.com/tonistiigi/xx | https://hub.docker.com/r/tonistiigi/xx/tags xx_image_digest: "sha256:1978e7a58a1777cb0ef0dde76bad60b7914b21da57cfa88047875e4f364297aa" -rust_version: 1.82.0 # Rust version to be used +rust_version: 1.83.0 # Rust version to be used debian_version: bookworm # Debian release name to be used alpine_version: "3.20" # Alpine version to be used # For which platforms/architectures will we try to build images diff --git a/docker/Dockerfile.alpine b/docker/Dockerfile.alpine index c6c85003..8713e7c4 100644 --- a/docker/Dockerfile.alpine +++ b/docker/Dockerfile.alpine @@ -32,10 +32,10 @@ FROM --platform=linux/amd64 docker.io/vaultwarden/web-vault@sha256:409ab328ca931 ########################## ALPINE BUILD IMAGES ########################## ## NOTE: The Alpine Base Images do not support other platforms then linux/amd64 ## And for Alpine we define all build images here, they will only be loaded when actually used -FROM --platform=linux/amd64 ghcr.io/blackdex/rust-musl:x86_64-musl-stable-1.82.0 AS build_amd64 -FROM --platform=linux/amd64 ghcr.io/blackdex/rust-musl:aarch64-musl-stable-1.82.0 AS build_arm64 -FROM --platform=linux/amd64 ghcr.io/blackdex/rust-musl:armv7-musleabihf-stable-1.82.0 AS build_armv7 -FROM --platform=linux/amd64 ghcr.io/blackdex/rust-musl:arm-musleabi-stable-1.82.0 AS build_armv6 +FROM --platform=linux/amd64 ghcr.io/blackdex/rust-musl:x86_64-musl-stable-1.83.0 AS build_amd64 +FROM --platform=linux/amd64 ghcr.io/blackdex/rust-musl:aarch64-musl-stable-1.83.0 AS build_arm64 +FROM --platform=linux/amd64 ghcr.io/blackdex/rust-musl:armv7-musleabihf-stable-1.83.0 AS build_armv7 +FROM --platform=linux/amd64 ghcr.io/blackdex/rust-musl:arm-musleabi-stable-1.83.0 AS build_armv6 ########################## BUILD IMAGE ########################## # hadolint ignore=DL3006 diff --git a/docker/Dockerfile.debian b/docker/Dockerfile.debian index eb502eb2..69404a2e 100644 --- a/docker/Dockerfile.debian +++ b/docker/Dockerfile.debian @@ -36,7 +36,7 @@ FROM --platform=linux/amd64 docker.io/tonistiigi/xx@sha256:1978e7a58a1777cb0ef0d ########################## BUILD IMAGE ########################## # hadolint ignore=DL3006 -FROM --platform=$BUILDPLATFORM docker.io/library/rust:1.82.0-slim-bookworm AS build +FROM --platform=$BUILDPLATFORM docker.io/library/rust:1.83.0-slim-bookworm AS build COPY --from=xx / / ARG TARGETARCH ARG TARGETVARIANT diff --git a/rust-toolchain.toml b/rust-toolchain.toml index a74a99f4..2c76f619 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,4 +1,4 @@ [toolchain] -channel = "1.82.0" +channel = "1.83.0" components = [ "rustfmt", "clippy" ] profile = "minimal" diff --git a/src/api/icons.rs b/src/api/icons.rs index ebcace6d..921d48b9 100644 --- a/src/api/icons.rs +++ b/src/api/icons.rs @@ -19,7 +19,7 @@ use tokio::{ io::{AsyncReadExt, AsyncWriteExt}, }; -use html5gum::{Emitter, HtmlString, InfallibleTokenizer, Readable, StringReader, Tokenizer}; +use html5gum::{Emitter, HtmlString, Readable, StringReader, Tokenizer}; use crate::{ error::Error, @@ -261,11 +261,7 @@ impl Icon { } } -fn get_favicons_node( - dom: InfallibleTokenizer, FaviconEmitter>, - icons: &mut Vec, - url: &url::Url, -) { +fn get_favicons_node(dom: Tokenizer, FaviconEmitter>, icons: &mut Vec, url: &url::Url) { const TAG_LINK: &[u8] = b"link"; const TAG_BASE: &[u8] = b"base"; const TAG_HEAD: &[u8] = b"head"; @@ -274,7 +270,7 @@ fn get_favicons_node( let mut base_url = url.clone(); let mut icon_tags: Vec = Vec::new(); - for token in dom { + for Ok(token) in dom { let tag_name: &[u8] = &token.tag.name; match tag_name { TAG_LINK => { @@ -401,7 +397,7 @@ async fn get_icon_url(domain: &str) -> Result { // 384KB should be more than enough for the HTML, though as we only really need the HTML header. let limited_reader = stream_to_bytes_limit(content, 384 * 1024).await?.to_vec(); - let dom = Tokenizer::new_with_emitter(limited_reader.to_reader(), FaviconEmitter::default()).infallible(); + let dom = Tokenizer::new_with_emitter(limited_reader.to_reader(), FaviconEmitter::default()); get_favicons_node(dom, &mut iconlist, &url); } else { // Add the default favicon.ico to the list with just the given domain diff --git a/src/db/mod.rs b/src/db/mod.rs index fe1ab79b..464be561 100644 --- a/src/db/mod.rs +++ b/src/db/mod.rs @@ -373,24 +373,18 @@ pub async fn backup_database(conn: &mut DbConn) -> Result { err!("PostgreSQL and MySQL/MariaDB do not support this backup feature"); } sqlite { - backup_sqlite_database(conn) + let db_url = CONFIG.database_url(); + let db_path = std::path::Path::new(&db_url).parent().unwrap(); + let backup_file = db_path + .join(format!("db_{}.sqlite3", chrono::Utc::now().format("%Y%m%d_%H%M%S"))) + .to_string_lossy() + .into_owned(); + diesel::sql_query(format!("VACUUM INTO '{backup_file}'")).execute(conn)?; + Ok(backup_file) } } } -#[cfg(sqlite)] -pub fn backup_sqlite_database(conn: &mut diesel::sqlite::SqliteConnection) -> Result { - use diesel::RunQueryDsl; - let db_url = CONFIG.database_url(); - let db_path = std::path::Path::new(&db_url).parent().unwrap(); - let backup_file = db_path - .join(format!("db_{}.sqlite3", chrono::Utc::now().format("%Y%m%d_%H%M%S"))) - .to_string_lossy() - .into_owned(); - diesel::sql_query(format!("VACUUM INTO '{backup_file}'")).execute(conn)?; - Ok(backup_file) -} - /// Get the SQL Server version pub async fn get_sql_server_version(conn: &mut DbConn) -> String { db_run! {@raw conn: diff --git a/src/main.rs b/src/main.rs index 7e180e2e..3a151cdf 100644 --- a/src/main.rs +++ b/src/main.rs @@ -67,7 +67,7 @@ pub use util::is_running_in_container; #[rocket::main] async fn main() -> Result<(), Error> { - parse_args(); + parse_args().await; launch_info(); let level = init_logging()?; @@ -115,7 +115,7 @@ PRESETS: m= t= p= pub const VERSION: Option<&str> = option_env!("VW_VERSION"); -fn parse_args() { +async fn parse_args() { let mut pargs = pico_args::Arguments::from_env(); let version = VERSION.unwrap_or("(Version info from Git not present)"); @@ -186,7 +186,7 @@ fn parse_args() { exit(1); } } else if command == "backup" { - match backup_sqlite() { + match backup_sqlite().await { Ok(f) => { println!("Backup to '{f}' was successful"); exit(0); @@ -201,25 +201,20 @@ fn parse_args() { } } -fn backup_sqlite() -> Result { - #[cfg(sqlite)] - { - use crate::db::{backup_sqlite_database, DbConnType}; - if DbConnType::from_url(&CONFIG.database_url()).map(|t| t == DbConnType::sqlite).unwrap_or(false) { - use diesel::Connection; - let url = CONFIG.database_url(); +async fn backup_sqlite() -> Result { + use crate::db::{backup_database, DbConnType}; + if DbConnType::from_url(&CONFIG.database_url()).map(|t| t == DbConnType::sqlite).unwrap_or(false) { + // Establish a connection to the sqlite database + let mut conn = db::DbPool::from_config() + .expect("SQLite database connection failed") + .get() + .await + .expect("Unable to get SQLite db pool"); - // Establish a connection to the sqlite database - let mut conn = diesel::sqlite::SqliteConnection::establish(&url)?; - let backup_file = backup_sqlite_database(&mut conn)?; - Ok(backup_file) - } else { - err_silent!("The database type is not SQLite. Backups only works for SQLite databases") - } - } - #[cfg(not(sqlite))] - { - err_silent!("The 'sqlite' feature is not enabled. Backups only works for SQLite databases") + let backup_file = backup_database(&mut conn).await?; + Ok(backup_file) + } else { + err_silent!("The database type is not SQLite. Backups only works for SQLite databases") } } @@ -610,7 +605,7 @@ async fn launch_rocket(pool: db::DbPool, extra_debug: bool) -> Result<(), Error> // If we need more signals to act upon, we might want to use select! here. // With only one item to listen for this is enough. let _ = signal_user1.recv().await; - match backup_sqlite() { + match backup_sqlite().await { Ok(f) => info!("Backup to '{f}' was successful"), Err(e) => error!("Backup failed. {e:?}"), } From d7adce97df5dec9b121f4bee67be78a955247cfb Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 6 Dec 2024 12:05:52 +0200 Subject: [PATCH 018/286] Update Alpine to version 3.21 (#5256) --- docker/DockerSettings.yaml | 2 +- docker/Dockerfile.alpine | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docker/DockerSettings.yaml b/docker/DockerSettings.yaml index dbfdbed3..6896c3dd 100644 --- a/docker/DockerSettings.yaml +++ b/docker/DockerSettings.yaml @@ -7,7 +7,7 @@ vault_image_digest: "sha256:409ab328ca931439cb916b388a4bb784bd44220717aaf74cf716 xx_image_digest: "sha256:1978e7a58a1777cb0ef0dde76bad60b7914b21da57cfa88047875e4f364297aa" rust_version: 1.83.0 # Rust version to be used debian_version: bookworm # Debian release name to be used -alpine_version: "3.20" # Alpine version to be used +alpine_version: "3.21" # Alpine version to be used # For which platforms/architectures will we try to build images platforms: ["linux/amd64", "linux/arm64", "linux/arm/v7", "linux/arm/v6"] # Determine the build images per OS/Arch diff --git a/docker/Dockerfile.alpine b/docker/Dockerfile.alpine index 8713e7c4..77915fd8 100644 --- a/docker/Dockerfile.alpine +++ b/docker/Dockerfile.alpine @@ -126,7 +126,7 @@ RUN source /env-cargo && \ # To uninstall: docker run --privileged --rm tonistiigi/binfmt --uninstall 'qemu-*' # # We need to add `--platform` here, because of a podman bug: https://github.com/containers/buildah/issues/4742 -FROM --platform=$TARGETPLATFORM docker.io/library/alpine:3.20 +FROM --platform=$TARGETPLATFORM docker.io/library/alpine:3.21 ENV ROCKET_PROFILE="release" \ ROCKET_ADDRESS=0.0.0.0 \ From c9860af11ccedfa9b347e22652d75b862eeba391 Mon Sep 17 00:00:00 2001 From: Mathijs van Veluw Date: Sun, 8 Dec 2024 21:48:19 +0100 Subject: [PATCH 019/286] Fix another sync issue with native clients (#5259) The `reprompt` value somehow sometimes has a value of `4`. This isn't a valid value, and doesn't cause issues with other clients, but the native clients are more strict. This commit fixes this by validating the value before storing and returning. Signed-off-by: BlackDex --- src/api/core/ciphers.rs | 2 +- src/db/models/cipher.rs | 5 ++--- src/db/models/mod.rs | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/api/core/ciphers.rs b/src/api/core/ciphers.rs index aa390e5e..46d65b53 100644 --- a/src/api/core/ciphers.rs +++ b/src/api/core/ciphers.rs @@ -511,7 +511,7 @@ pub async fn update_cipher_from_data( cipher.fields = data.fields.map(|f| _clean_cipher_data(f).to_string()); cipher.data = type_data.to_string(); cipher.password_history = data.password_history.map(|f| f.to_string()); - cipher.reprompt = data.reprompt; + cipher.reprompt = data.reprompt.filter(|r| *r == RepromptType::None as i32 || *r == RepromptType::Password as i32); cipher.save(conn).await?; cipher.move_to_folder(data.folder_id, &headers.user.uuid, conn).await?; diff --git a/src/db/models/cipher.rs b/src/db/models/cipher.rs index 75d54df2..048cb4d1 100644 --- a/src/db/models/cipher.rs +++ b/src/db/models/cipher.rs @@ -46,10 +46,9 @@ db_object! { } } -#[allow(dead_code)] pub enum RepromptType { None = 0, - Password = 1, // not currently used in server + Password = 1, } /// Local methods @@ -296,7 +295,7 @@ impl Cipher { "creationDate": format_date(&self.created_at), "revisionDate": format_date(&self.updated_at), "deletedDate": self.deleted_at.map_or(Value::Null, |d| Value::String(format_date(&d))), - "reprompt": self.reprompt.unwrap_or(RepromptType::None as i32), + "reprompt": self.reprompt.filter(|r| *r == RepromptType::None as i32 || *r == RepromptType::Password as i32).unwrap_or(RepromptType::None as i32), "organizationId": self.organization_uuid, "key": self.key, "attachments": attachments_json, diff --git a/src/db/models/mod.rs b/src/db/models/mod.rs index c336cb1a..43595bee 100644 --- a/src/db/models/mod.rs +++ b/src/db/models/mod.rs @@ -18,7 +18,7 @@ mod user; pub use self::attachment::Attachment; pub use self::auth_request::AuthRequest; -pub use self::cipher::Cipher; +pub use self::cipher::{Cipher, RepromptType}; pub use self::collection::{Collection, CollectionCipher, CollectionUser}; pub use self::device::{Device, DeviceType}; pub use self::emergency_access::{EmergencyAccess, EmergencyAccessStatus, EmergencyAccessType}; From 620ad9233159c443acd91ec0c3828332b2c868cd Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 10 Dec 2024 18:59:28 +0200 Subject: [PATCH 020/286] Update crates (#5268) - fixes CVE-2024-12224 --- Cargo.lock | 173 +++++++++++++++++++++++++---------------------------- Cargo.toml | 8 +-- 2 files changed, 87 insertions(+), 94 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 497a38ac..2107b75c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -352,9 +352,9 @@ checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" [[package]] name = "bigdecimal" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f850665a0385e070b64c38d2354e6c104c8479c59868d1e48a0c13ee2c7a1c1" +checksum = "7f31f3af01c5c65a07985c804d3366560e6fa7883d640a122819b14ec327482c" dependencies = [ "autocfg", "libm", @@ -464,7 +464,7 @@ dependencies = [ "futures", "hashbrown 0.14.5", "once_cell", - "thiserror", + "thiserror 1.0.69", "tokio", "web-time", ] @@ -489,9 +489,9 @@ checksum = "ade8366b8bd5ba243f0a58f036cc0ca8a2f069cff1a2351ef1cac6b083e16fc0" [[package]] name = "cc" -version = "1.2.2" +version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f34d93e62b03caf570cccc334cbc6c2fceca82f39211051345108adcba3eebdc" +checksum = "27f657647bcff5394bf56c7317665bbf790a137a50eaaa5c6bfbb9e27a518f2d" dependencies = [ "shlex", ] @@ -504,9 +504,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.38" +version = "0.4.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" dependencies = [ "android-tzdata", "iana-time-zone", @@ -580,7 +580,7 @@ checksum = "2eac901828f88a5241ee0600950ab981148a18f2f756900ffba1b125ca6a3ef9" dependencies = [ "cookie", "document-features", - "idna 1.0.3", + "idna", "log", "publicsuffix", "serde", @@ -963,9 +963,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "fern" @@ -1271,7 +1271,7 @@ dependencies = [ "pest_derive", "serde", "serde_json", - "thiserror", + "thiserror 1.0.69", "walkdir", ] @@ -1311,9 +1311,9 @@ checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" [[package]] name = "hickory-proto" -version = "0.24.1" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07698b8420e2f0d6447a436ba999ec85d8fbf2a398bbd737b82cac4a2e96e512" +checksum = "447afdcdb8afb9d0a852af6dc65d9b285ce720ed7a59e42a8bf2e931c67bc1b5" dependencies = [ "async-trait", "cfg-if", @@ -1322,11 +1322,11 @@ dependencies = [ "futures-channel", "futures-io", "futures-util", - "idna 0.4.0", + "idna", "ipnet", "once_cell", "rand", - "thiserror", + "thiserror 1.0.69", "tinyvec", "tokio", "tracing", @@ -1335,9 +1335,9 @@ dependencies = [ [[package]] name = "hickory-resolver" -version = "0.24.1" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28757f23aa75c98f254cf0405e6d8c25b831b32921b050a66692427679b1f243" +checksum = "0a2e2aba9c389ce5267d31cf1e4dace82390ae276b0b364ea55630b1fa1b44b4" dependencies = [ "cfg-if", "futures-util", @@ -1349,7 +1349,7 @@ dependencies = [ "rand", "resolv-conf", "smallvec", - "thiserror", + "thiserror 1.0.69", "tokio", "tracing", ] @@ -1527,7 +1527,7 @@ dependencies = [ "rustls 0.23.19", "rustls-pki-types", "tokio", - "tokio-rustls 0.26.0", + "tokio-rustls 0.26.1", "tower-service", ] @@ -1713,16 +1713,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" -[[package]] -name = "idna" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - [[package]] name = "idna" version = "1.0.3" @@ -1815,9 +1805,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.74" +version = "0.3.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a865e038f7f6ed956f788f0d7d60c541fff74c7bd74272c5d4cf15c63743e705" +checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7" dependencies = [ "once_cell", "wasm-bindgen", @@ -1864,9 +1854,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "lettre" -version = "0.11.10" +version = "0.11.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0161e452348e399deb685ba05e55ee116cae9410f4f51fe42d597361444521d9" +checksum = "ab4c9a167ff73df98a5ecc07e8bf5ce90b583665da3d1762eb1f775ad4d0d6f5" dependencies = [ "async-std", "async-trait", @@ -1879,7 +1869,7 @@ dependencies = [ "futures-util", "hostname 0.4.0", "httpdate", - "idna 1.0.3", + "idna", "mime", "native-tls", "nom", @@ -1895,9 +1885,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.167" +version = "0.2.168" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09d6582e104315a817dff97f75133544b2e094ee22447d2acf4a74e189ba06fc" +checksum = "5aaeb2981e0606ca11d79718f8bb01164f1d6ed75080182d3abf017e6d244b6d" [[package]] name = "libm" @@ -2404,20 +2394,20 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.7.14" +version = "2.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "879952a81a83930934cbf1786752d6dedc3b1f29e8f8fb2ad1d0a36f377cf442" +checksum = "8b7cafe60d6cf8e62e1b9b2ea516a089c008945bb5a275416789e7db0bc199dc" dependencies = [ "memchr", - "thiserror", + "thiserror 2.0.6", "ucd-trie", ] [[package]] name = "pest_derive" -version = "2.7.14" +version = "2.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d214365f632b123a47fd913301e14c946c61d1c183ee245fa76eb752e59a02dd" +checksum = "816518421cfc6887a0d62bf441b6ffb4536fcc926395a69e1a85852d4363f57e" dependencies = [ "pest", "pest_generator", @@ -2425,9 +2415,9 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.7.14" +version = "2.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb55586734301717aea2ac313f50b2eb8f60d2fc3dc01d190eefa2e625f60c4e" +checksum = "7d1396fd3a870fc7838768d171b4616d5c91f6cc25e377b673d714567d99377b" dependencies = [ "pest", "pest_meta", @@ -2438,9 +2428,9 @@ dependencies = [ [[package]] name = "pest_meta" -version = "2.7.14" +version = "2.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b75da2a70cf4d9cb76833c990ac9cd3923c9a8905a8929789ce347c84564d03d" +checksum = "e1e58089ea25d717bfd31fb534e4f3afcc2cc569c70de3e239778991ea3b7dea" dependencies = [ "once_cell", "pest", @@ -2622,7 +2612,7 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f42ea446cab60335f76979ec15e12619a2165b5ae2c12166bef27d283a9fadf" dependencies = [ - "idna 1.0.3", + "idna", "psl-types", ] @@ -3016,15 +3006,15 @@ checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustix" -version = "0.38.41" +version = "0.38.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7f649912bc1495e167a6edee79151c84b1bad49748cb4f1f1167f459f6224f6" +checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85" dependencies = [ "bitflags", "errno", "libc", "linux-raw-sys", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -3314,7 +3304,7 @@ checksum = "adc4e5204eb1910f40f9cfa375f6f05b68c3abac4b6fd879c8ff5e7ae8a0a085" dependencies = [ "num-bigint", "num-traits", - "thiserror", + "thiserror 1.0.69", "time", ] @@ -3496,7 +3486,16 @@ version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fec2a1820ebd077e2b90c4df007bebf344cd394098a13c563957d0afc83ea47" +dependencies = [ + "thiserror-impl 2.0.6", ] [[package]] @@ -3510,6 +3509,17 @@ dependencies = [ "syn", ] +[[package]] +name = "thiserror-impl" +version = "2.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d65750cab40f4ff1929fb1ba509e9914eb756131cef4210da8d5d700d26f6312" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "thread_local" version = "1.1.8" @@ -3638,12 +3648,11 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.26.0" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" +checksum = "5f6d0975eaace0cf0fcadee4e4aaa5da15b5c079146f2cffb67c113be122bf37" dependencies = [ "rustls 0.23.19", - "rustls-pki-types", "tokio", ] @@ -3655,15 +3664,15 @@ checksum = "0d4770b8024672c1101b3f6733eab95b18007dbe0847a8afe341fcf79e06043f" dependencies = [ "either", "futures-util", - "thiserror", + "thiserror 1.0.69", "tokio", ] [[package]] name = "tokio-stream" -version = "0.1.16" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f4e6ce100d0eb49a2734f8c0812bcd324cf357d21810932c5df6b96ef2b86f1" +checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" dependencies = [ "futures-core", "pin-project-lite", @@ -3829,7 +3838,7 @@ dependencies = [ "log", "rand", "sha1", - "thiserror", + "thiserror 1.0.69", "url", "utf-8", ] @@ -3865,27 +3874,12 @@ dependencies = [ "version_check", ] -[[package]] -name = "unicode-bidi" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893" - [[package]] name = "unicode-ident" version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" -[[package]] -name = "unicode-normalization" -version = "0.1.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" -dependencies = [ - "tinyvec", -] - [[package]] name = "unicode-xid" version = "0.2.6" @@ -3905,7 +3899,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" dependencies = [ "form_urlencoded", - "idna 1.0.3", + "idna", "percent-encoding", "serde", ] @@ -4051,9 +4045,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.97" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d15e63b4482863c109d70a7b8706c1e364eb6ea449b201a76c5b89cedcec2d5c" +checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396" dependencies = [ "cfg-if", "once_cell", @@ -4062,13 +4056,12 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.97" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d36ef12e3aaca16ddd3f67922bc63e48e953f126de60bd33ccc0101ef9998cd" +checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79" dependencies = [ "bumpalo", "log", - "once_cell", "proc-macro2", "quote", "syn", @@ -4077,9 +4070,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.47" +version = "0.4.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dfaf8f50e5f293737ee323940c7d8b08a66a95a419223d9f41610ca08b0833d" +checksum = "38176d9b44ea84e9184eff0bc34cc167ed044f816accfe5922e54d84cf48eca2" dependencies = [ "cfg-if", "js-sys", @@ -4090,9 +4083,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.97" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "705440e08b42d3e4b36de7d66c944be628d579796b8090bfa3471478a2260051" +checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -4100,9 +4093,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.97" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98c9ae5a76e46f4deecd0f0255cc223cfa18dc9b261213b8aa0c7b36f61b3f1d" +checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" dependencies = [ "proc-macro2", "quote", @@ -4113,9 +4106,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.97" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ee99da9c5ba11bd675621338ef6fa52296b76b83305e9b6e5c77d4c286d6d49" +checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6" [[package]] name = "wasm-streams" @@ -4132,9 +4125,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.74" +version = "0.3.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a98bc3c33f0fe7e59ad7cd041b89034fa82a7c2d4365ca538dda6cdaf513863c" +checksum = "04dd7223427d52553d3702c004d3b2fe07c148165faa56313cb00211e31c12bc" dependencies = [ "js-sys", "wasm-bindgen", @@ -4164,7 +4157,7 @@ dependencies = [ "serde_cbor", "serde_derive", "serde_json", - "thiserror", + "thiserror 1.0.69", "tracing", "url", ] diff --git a/Cargo.toml b/Cargo.toml index cae715e6..63b04f9d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -53,7 +53,7 @@ once_cell = "1.20.2" # Numerical libraries num-traits = "0.2.19" num-derive = "0.4.2" -bigdecimal = "0.4.6" +bigdecimal = "0.4.7" # Web framework rocket = { version = "0.5.1", features = ["tls", "json"], default-features = false } @@ -89,7 +89,7 @@ ring = "0.17.8" uuid = { version = "1.11.0", features = ["v4"] } # Date and time libraries -chrono = { version = "0.4.38", features = ["clock", "serde"], default-features = false } +chrono = { version = "0.4.39", features = ["clock", "serde"], default-features = false } chrono-tz = "0.10.0" time = "0.3.37" @@ -115,7 +115,7 @@ webauthn-rs = "0.3.2" url = "2.5.4" # Email libraries -lettre = { version = "0.11.10", features = ["smtp-transport", "sendmail-transport", "builder", "serde", "tokio1-native-tls", "hostname", "tracing", "tokio1"], default-features = false } +lettre = { version = "0.11.11", features = ["smtp-transport", "sendmail-transport", "builder", "serde", "tokio1-native-tls", "hostname", "tracing", "tokio1"], default-features = false } percent-encoding = "2.3.1" # URL encoding library used for URL's in the emails email_address = "0.2.9" @@ -124,7 +124,7 @@ handlebars = { version = "6.2.0", features = ["dir_source"] } # HTTP client (Used for favicons, version check, DUO and HIBP API) reqwest = { version = "0.12.9", features = ["native-tls-alpn", "stream", "json", "gzip", "brotli", "socks", "cookies"] } -hickory-resolver = "0.24.1" +hickory-resolver = "0.24.2" # Favicon extraction libraries html5gum = "0.7.0" From 45e5f06b86c66d30ece0cf9169d3a5b65a87e380 Mon Sep 17 00:00:00 2001 From: Mathijs van Veluw Date: Tue, 10 Dec 2024 21:52:12 +0100 Subject: [PATCH 021/286] Some Backend Admin fixes and updates (#5272) * Some Backend Admin fixes and updates - Updated datatables - Added a `X-Robots-Tags` header to prevent indexing - Modified some layout settings - Added Websocket check to diagnostics - Added Security Header checks to diagnostics - Added Error page response checks to diagnostics - Modifed support string layout a bit Signed-off-by: BlackDex * Some small fixes Signed-off-by: BlackDex --------- Signed-off-by: BlackDex --- src/api/admin.rs | 7 + src/static/scripts/admin.css | 4 +- src/static/scripts/admin_diagnostics.js | 212 ++- src/static/scripts/datatables.css | 42 +- src/static/scripts/datatables.js | 1414 +++++++++++++------- src/static/templates/admin/diagnostics.hbs | 23 + src/static/templates/admin/users.hbs | 2 +- src/util.rs | 2 + 8 files changed, 1194 insertions(+), 512 deletions(-) diff --git a/src/api/admin.rs b/src/api/admin.rs index cc902e39..45eb5972 100644 --- a/src/api/admin.rs +++ b/src/api/admin.rs @@ -62,6 +62,7 @@ pub fn routes() -> Vec { diagnostics, get_diagnostics_config, resend_user_invite, + get_diagnostics_http, ] } @@ -713,6 +714,7 @@ async fn diagnostics(_token: AdminToken, ip_header: IpHeader, mut conn: DbConn) "ip_header_name": ip_header_name, "ip_header_config": &CONFIG.ip_header(), "uses_proxy": uses_proxy, + "enable_websocket": &CONFIG.enable_websocket(), "db_type": *DB_TYPE, "db_version": get_sql_server_version(&mut conn).await, "admin_url": format!("{}/diagnostics", admin_url()), @@ -734,6 +736,11 @@ fn get_diagnostics_config(_token: AdminToken) -> Json { Json(support_json) } +#[get("/diagnostics/http?")] +fn get_diagnostics_http(code: u16, _token: AdminToken) -> EmptyResult { + err_code!(format!("Testing error {code} response"), code); +} + #[post("/config", data = "")] fn post_config(data: Json, _token: AdminToken) -> EmptyResult { let data: ConfigBuilder = data.into_inner(); diff --git a/src/static/scripts/admin.css b/src/static/scripts/admin.css index 1db8d4c0..ee035ac4 100644 --- a/src/static/scripts/admin.css +++ b/src/static/scripts/admin.css @@ -38,8 +38,8 @@ img { max-width: 130px; } #users-table .vw-actions, #orgs-table .vw-actions { - min-width: 130px; - max-width: 130px; + min-width: 135px; + max-width: 140px; } #users-table .vw-org-cell { max-height: 120px; diff --git a/src/static/scripts/admin_diagnostics.js b/src/static/scripts/admin_diagnostics.js index 6a178e4b..258df5e1 100644 --- a/src/static/scripts/admin_diagnostics.js +++ b/src/static/scripts/admin_diagnostics.js @@ -7,6 +7,8 @@ var timeCheck = false; var ntpTimeCheck = false; var domainCheck = false; var httpsCheck = false; +var websocketCheck = false; +var httpResponseCheck = false; // ================================ // Date & Time Check @@ -76,18 +78,15 @@ async function generateSupportString(event, dj) { event.preventDefault(); event.stopPropagation(); - let supportString = "### Your environment (Generated via diagnostics page)\n"; + let supportString = "### Your environment (Generated via diagnostics page)\n\n"; supportString += `* Vaultwarden version: v${dj.current_release}\n`; supportString += `* Web-vault version: v${dj.web_vault_version}\n`; supportString += `* OS/Arch: ${dj.host_os}/${dj.host_arch}\n`; supportString += `* Running within a container: ${dj.running_within_container} (Base: ${dj.container_base_image})\n`; - supportString += "* Environment settings overridden: "; - if (dj.overrides != "") { - supportString += "true\n"; - } else { - supportString += "false\n"; - } + supportString += `* Database type: ${dj.db_type}\n`; + supportString += `* Database version: ${dj.db_version}\n`; + supportString += `* Environment settings overridden!: ${dj.overrides !== ""}\n`; supportString += `* Uses a reverse proxy: ${dj.ip_header_exists}\n`; if (dj.ip_header_exists) { supportString += `* IP Header check: ${dj.ip_header_match} (${dj.ip_header_name})\n`; @@ -99,11 +98,12 @@ async function generateSupportString(event, dj) { supportString += `* Server/NTP Time Check: ${ntpTimeCheck}\n`; supportString += `* Domain Configuration Check: ${domainCheck}\n`; supportString += `* HTTPS Check: ${httpsCheck}\n`; - supportString += `* Database type: ${dj.db_type}\n`; - supportString += `* Database version: ${dj.db_version}\n`; - supportString += "* Clients used: \n"; - supportString += "* Reverse proxy and version: \n"; - supportString += "* Other relevant information: \n"; + if (dj.enable_websocket) { + supportString += `* Websocket Check: ${websocketCheck}\n`; + } else { + supportString += "* Websocket Check: disabled\n"; + } + supportString += `* HTTP Response Checks: ${httpResponseCheck}\n`; const jsonResponse = await fetch(`${BASE_URL}/admin/diagnostics/config`, { "headers": { "Accept": "application/json" } @@ -113,10 +113,30 @@ async function generateSupportString(event, dj) { throw new Error(jsonResponse); } const configJson = await jsonResponse.json(); - supportString += "\n### Config (Generated via diagnostics page)\n
Show Running Config\n"; - supportString += `\n**Environment settings which are overridden:** ${dj.overrides}\n`; - supportString += "\n\n```json\n" + JSON.stringify(configJson, undefined, 2) + "\n```\n
\n"; + // Start Config and Details section within a details block which is collapsed by default + supportString += "\n### Config & Details (Generated via diagnostics page)\n\n"; + supportString += "
Show Config & Details\n"; + + // Add overrides if they exists + if (dj.overrides != "") { + supportString += `\n**Environment settings which are overridden:** ${dj.overrides}\n`; + } + + // Add http response check messages if they exists + if (httpResponseCheck === false) { + supportString += "\n**Failed HTTP Checks:**\n"; + // We use `innerText` here since that will convert
into new-lines + supportString += "\n```yaml\n" + document.getElementById("http-response-errors").innerText.trim() + "\n```\n"; + } + + // Add the current config in json form + supportString += "\n**Config:**\n"; + supportString += "\n```json\n" + JSON.stringify(configJson, undefined, 2) + "\n```\n"; + + supportString += "\n
\n"; + + // Add the support string to the textbox so it can be viewed and copied document.getElementById("support-string").textContent = supportString; document.getElementById("support-string").classList.remove("d-none"); document.getElementById("copy-support").classList.remove("d-none"); @@ -199,6 +219,162 @@ function checkDns(dns_resolved) { } } +async function fetchCheckUrl(url) { + try { + const response = await fetch(url); + return { headers: response.headers, status: response.status, text: await response.text() }; + } catch (error) { + console.error(`Error fetching ${url}: ${error}`); + return { error }; + } +} + +function checkSecurityHeaders(headers, omit) { + let securityHeaders = { + "x-frame-options": ["SAMEORIGIN"], + "x-content-type-options": ["nosniff"], + "referrer-policy": ["same-origin"], + "x-xss-protection": ["0"], + "x-robots-tag": ["noindex", "nofollow"], + "content-security-policy": [ + "default-src 'self'", + "base-uri 'self'", + "form-action 'self'", + "object-src 'self' blob:", + "script-src 'self' 'wasm-unsafe-eval'", + "style-src 'self' 'unsafe-inline'", + "child-src 'self' https://*.duosecurity.com https://*.duofederal.com", + "frame-src 'self' https://*.duosecurity.com https://*.duofederal.com", + "frame-ancestors 'self' chrome-extension://nngceckbapebfimnlniiiahkandclblb chrome-extension://jbkfoedolllekgbhcbcoahefnbanhhlh moz-extension://*", + "img-src 'self' data: https://haveibeenpwned.com", + "connect-src 'self' https://api.pwnedpasswords.com https://api.2fa.directory https://app.simplelogin.io/api/ https://app.addy.io/api/ https://api.fastmail.com/ https://api.forwardemail.net", + ] + }; + + let messages = []; + for (let header in securityHeaders) { + // Skip some headers for specific endpoints if needed + if (typeof omit === "object" && omit.includes(header) === true) { + continue; + } + // If the header exists, check if the contents matches what we expect it to be + let headerValue = headers.get(header); + if (headerValue !== null) { + securityHeaders[header].forEach((expectedValue) => { + if (headerValue.indexOf(expectedValue) === -1) { + messages.push(`'${header}' does not contain '${expectedValue}'`); + } + }); + } else { + messages.push(`'${header}' is missing!`); + } + } + return messages; +} + +async function checkHttpResponse() { + const [apiConfig, webauthnConnector, notFound, notFoundApi, badRequest, unauthorized, forbidden] = await Promise.all([ + fetchCheckUrl(`${BASE_URL}/api/config`), + fetchCheckUrl(`${BASE_URL}/webauthn-connector.html`), + fetchCheckUrl(`${BASE_URL}/admin/does-not-exist`), + fetchCheckUrl(`${BASE_URL}/admin/diagnostics/http?code=404`), + fetchCheckUrl(`${BASE_URL}/admin/diagnostics/http?code=400`), + fetchCheckUrl(`${BASE_URL}/admin/diagnostics/http?code=401`), + fetchCheckUrl(`${BASE_URL}/admin/diagnostics/http?code=403`), + ]); + + const respErrorElm = document.getElementById("http-response-errors"); + + // Check and validate the default API header responses + let apiErrors = checkSecurityHeaders(apiConfig.headers); + if (apiErrors.length >= 1) { + respErrorElm.innerHTML += "API calls:
"; + apiErrors.forEach((errMsg) => { + respErrorElm.innerHTML += `Header: ${errMsg}
`; + }); + } + + // Check the special `-connector.html` headers, these should have some headers omitted. + const omitConnectorHeaders = ["x-frame-options", "content-security-policy"]; + let connectorErrors = checkSecurityHeaders(webauthnConnector.headers, omitConnectorHeaders); + omitConnectorHeaders.forEach((header) => { + if (webauthnConnector.headers.get(header) !== null) { + connectorErrors.push(`'${header}' is present while it should not`); + } + }); + if (connectorErrors.length >= 1) { + respErrorElm.innerHTML += "2FA Connector calls:
"; + connectorErrors.forEach((errMsg) => { + respErrorElm.innerHTML += `Header: ${errMsg}
`; + }); + } + + // Check specific error code responses if they are not re-written by a reverse proxy + let responseErrors = []; + if (notFound.status !== 404 || notFound.text.indexOf("return to the web-vault") === -1) { + responseErrors.push("404 (Not Found) HTML is invalid"); + } + + if (notFoundApi.status !== 404 || notFoundApi.text.indexOf("\"message\":\"Testing error 404 response\",") === -1) { + responseErrors.push("404 (Not Found) JSON is invalid"); + } + + if (badRequest.status !== 400 || badRequest.text.indexOf("\"message\":\"Testing error 400 response\",") === -1) { + responseErrors.push("400 (Bad Request) is invalid"); + } + + if (unauthorized.status !== 401 || unauthorized.text.indexOf("\"message\":\"Testing error 401 response\",") === -1) { + responseErrors.push("401 (Unauthorized) is invalid"); + } + + if (forbidden.status !== 403 || forbidden.text.indexOf("\"message\":\"Testing error 403 response\",") === -1) { + responseErrors.push("403 (Forbidden) is invalid"); + } + + if (responseErrors.length >= 1) { + respErrorElm.innerHTML += "HTTP error responses:
"; + responseErrors.forEach((errMsg) => { + respErrorElm.innerHTML += `Response to: ${errMsg}
`; + }); + } + + if (responseErrors.length >= 1 || connectorErrors.length >= 1 || apiErrors.length >= 1) { + document.getElementById("http-response-warning").classList.remove("d-none"); + } else { + httpResponseCheck = true; + document.getElementById("http-response-success").classList.remove("d-none"); + } +} + +async function fetchWsUrl(wsUrl) { + return new Promise((resolve, reject) => { + try { + const ws = new WebSocket(wsUrl); + ws.onopen = () => { + ws.close(); + resolve(true); + }; + + ws.onerror = () => { + reject(false); + }; + } catch (_) { + reject(false); + } + }); +} + +async function checkWebsocketConnection() { + // Test Websocket connections via the anonymous (login with device) connection + const isConnected = await fetchWsUrl(`${BASE_URL}/notifications/anonymous-hub?token=admin-diagnostics`).catch(() => false); + if (isConnected) { + websocketCheck = true; + document.getElementById("websocket-success").classList.remove("d-none"); + } else { + document.getElementById("websocket-error").classList.remove("d-none"); + } +} + function init(dj) { // Time check document.getElementById("time-browser-string").textContent = browserUTC; @@ -225,6 +401,12 @@ function init(dj) { // DNS Check checkDns(dj.dns_resolved); + + checkHttpResponse(); + + if (dj.enable_websocket) { + checkWebsocketConnection(); + } } // onLoad events diff --git a/src/static/scripts/datatables.css b/src/static/scripts/datatables.css index 878e2347..195caa84 100644 --- a/src/static/scripts/datatables.css +++ b/src/static/scripts/datatables.css @@ -4,10 +4,10 @@ * * To rebuild or modify this file with the latest versions of the included * software please visit: - * https://datatables.net/download/#bs5/dt-2.0.8 + * https://datatables.net/download/#bs5/dt-2.1.8 * * Included libraries: - * DataTables 2.0.8 + * DataTables 2.1.8 */ @charset "UTF-8"; @@ -45,15 +45,21 @@ table.dataTable tr.dt-hasChild td.dt-control:before { } html.dark table.dataTable td.dt-control:before, -:root[data-bs-theme=dark] table.dataTable td.dt-control:before { +:root[data-bs-theme=dark] table.dataTable td.dt-control:before, +:root[data-theme=dark] table.dataTable td.dt-control:before { border-left-color: rgba(255, 255, 255, 0.5); } html.dark table.dataTable tr.dt-hasChild td.dt-control:before, -:root[data-bs-theme=dark] table.dataTable tr.dt-hasChild td.dt-control:before { +:root[data-bs-theme=dark] table.dataTable tr.dt-hasChild td.dt-control:before, +:root[data-theme=dark] table.dataTable tr.dt-hasChild td.dt-control:before { border-top-color: rgba(255, 255, 255, 0.5); border-left-color: transparent; } +div.dt-scroll { + width: 100%; +} + div.dt-scroll-body thead tr, div.dt-scroll-body tfoot tr { height: 0; @@ -377,6 +383,31 @@ table.table.dataTable.table-hover > tbody > tr.selected:hover > * { box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.975); } +div.dt-container div.dt-layout-start > *:not(:last-child) { + margin-right: 1em; +} +div.dt-container div.dt-layout-end > *:not(:first-child) { + margin-left: 1em; +} +div.dt-container div.dt-layout-full { + width: 100%; +} +div.dt-container div.dt-layout-full > *:only-child { + margin-left: auto; + margin-right: auto; +} +div.dt-container div.dt-layout-table > div { + display: block !important; +} + +@media screen and (max-width: 767px) { + div.dt-container div.dt-layout-start > *:not(:last-child) { + margin-right: 0; + } + div.dt-container div.dt-layout-end > *:not(:first-child) { + margin-left: 0; + } +} div.dt-container div.dt-length label { font-weight: normal; text-align: left; @@ -400,9 +431,6 @@ div.dt-container div.dt-search input { display: inline-block; width: auto; } -div.dt-container div.dt-info { - padding-top: 0.85em; -} div.dt-container div.dt-paging { margin: 0; } diff --git a/src/static/scripts/datatables.js b/src/static/scripts/datatables.js index 3d22cbde..d0361b54 100644 --- a/src/static/scripts/datatables.js +++ b/src/static/scripts/datatables.js @@ -4,20 +4,20 @@ * * To rebuild or modify this file with the latest versions of the included * software please visit: - * https://datatables.net/download/#bs5/dt-2.0.8 + * https://datatables.net/download/#bs5/dt-2.1.8 * * Included libraries: - * DataTables 2.0.8 + * DataTables 2.1.8 */ -/*! DataTables 2.0.8 +/*! DataTables 2.1.8 * © SpryMedia Ltd - datatables.net/license */ /** * @summary DataTables * @description Paginate, search and order HTML tables - * @version 2.0.8 + * @version 2.1.8 * @author SpryMedia Ltd * @contact www.datatables.net * @copyright SpryMedia Ltd. @@ -116,7 +116,6 @@ var i=0, iLen; var sId = this.getAttribute( 'id' ); - var bInitHandedOff = false; var defaults = DataTable.defaults; var $this = $(this); @@ -266,6 +265,8 @@ "rowId", "caption", "layout", + "orderDescReverse", + "typeDetect", [ "iCookieDuration", "iStateDuration" ], // backwards compat [ "oSearch", "oPreviousSearch" ], [ "aoSearchCols", "aoPreSearchCols" ], @@ -312,38 +313,14 @@ oSettings._iDisplayStart = oInit.iDisplayStart; } - /* Language definitions */ - var oLanguage = oSettings.oLanguage; - $.extend( true, oLanguage, oInit.oLanguage ); - - if ( oLanguage.sUrl ) + var defer = oInit.iDeferLoading; + if ( defer !== null ) { - /* Get the language definitions from a file - because this Ajax call makes the language - * get async to the remainder of this function we use bInitHandedOff to indicate that - * _fnInitialise will be fired by the returned Ajax handler, rather than the constructor - */ - $.ajax( { - dataType: 'json', - url: oLanguage.sUrl, - success: function ( json ) { - _fnCamelToHungarian( defaults.oLanguage, json ); - $.extend( true, oLanguage, json, oSettings.oInit.oLanguage ); + oSettings.deferLoading = true; - _fnCallbackFire( oSettings, null, 'i18n', [oSettings], true); - _fnInitialise( oSettings ); - }, - error: function () { - // Error occurred loading language file - _fnLog( oSettings, 0, 'i18n file loading error', 21 ); - - // continue on as best we can - _fnInitialise( oSettings ); - } - } ); - bInitHandedOff = true; - } - else { - _fnCallbackFire( oSettings, null, 'i18n', [oSettings]); + var tmp = Array.isArray(defer); + oSettings._iRecordsDisplay = tmp ? defer[0] : defer; + oSettings._iRecordsTotal = tmp ? defer[1] : defer; } /* @@ -410,113 +387,112 @@ } ); } - var features = oSettings.oFeatures; - var loadedInit = function () { - /* - * Sorting - * @todo For modularisation (1.11) this needs to do into a sort start up handler - */ - - // If aaSorting is not defined, then we use the first indicator in asSorting - // in case that has been altered, so the default sort reflects that option - if ( oInit.aaSorting === undefined ) { - var sorting = oSettings.aaSorting; - for ( i=0, iLen=sorting.length ; i').appendTo( $this ); - } - - caption.html( oSettings.caption ); - } - - // Store the caption side, so we can remove the element from the document - // when creating the element - if (caption.length) { - caption[0]._captionSide = caption.css('caption-side'); - oSettings.captionNode = caption[0]; - } - - if ( thead.length === 0 ) { - thead = $('').appendTo($this); - } - oSettings.nTHead = thead[0]; - $('tr', thead).addClass(oClasses.thead.row); - - var tbody = $this.children('tbody'); - if ( tbody.length === 0 ) { - tbody = $('').insertAfter(thead); - } - oSettings.nTBody = tbody[0]; - - var tfoot = $this.children('tfoot'); - if ( tfoot.length === 0 ) { - // If we are a scrolling table, and no footer has been given, then we need to create - // a tfoot element for the caption element to be appended to - tfoot = $('').appendTo($this); - } - oSettings.nTFoot = tfoot[0]; - $('tr', tfoot).addClass(oClasses.tfoot.row); - - // Check if there is data passing into the constructor - if ( oInit.aaData ) { - for ( i=0 ; i').appendTo( $this ); + } + + caption.html( oSettings.caption ); + } + + // Store the caption side, so we can remove the element from the document + // when creating the element + if (caption.length) { + caption[0]._captionSide = caption.css('caption-side'); + oSettings.captionNode = caption[0]; + } + + if ( thead.length === 0 ) { + thead = $('').appendTo($this); + } + oSettings.nTHead = thead[0]; + $('tr', thead).addClass(oClasses.thead.row); + + var tbody = $this.children('tbody'); + if ( tbody.length === 0 ) { + tbody = $('').insertAfter(thead); + } + oSettings.nTBody = tbody[0]; + + var tfoot = $this.children('tfoot'); + if ( tfoot.length === 0 ) { + // If we are a scrolling table, and no footer has been given, then we need to create + // a tfoot element for the caption element to be appended to + tfoot = $('').appendTo($this); + } + oSettings.nTFoot = tfoot[0]; + $('tr', tfoot).addClass(oClasses.tfoot.row); + + // Copy the data index array + oSettings.aiDisplay = oSettings.aiDisplayMaster.slice(); + + // Initialisation complete - table can be drawn + oSettings.bInitialised = true; + + // Language definitions + var oLanguage = oSettings.oLanguage; + $.extend( true, oLanguage, oInit.oLanguage ); + + if ( oLanguage.sUrl ) { + // Get the language definitions from a file + $.ajax( { + dataType: 'json', + url: oLanguage.sUrl, + success: function ( json ) { + _fnCamelToHungarian( defaults.oLanguage, json ); + $.extend( true, oLanguage, json, oSettings.oInit.oLanguage ); + + _fnCallbackFire( oSettings, null, 'i18n', [oSettings], true); + _fnInitialise( oSettings ); + }, + error: function () { + // Error occurred loading language file + _fnLog( oSettings, 0, 'i18n file loading error', 21 ); + + // Continue on as best we can + _fnInitialise( oSettings ); + } + } ); + } + else { + _fnCallbackFire( oSettings, null, 'i18n', [oSettings], true); + _fnInitialise( oSettings ); + } } ); _that = null; return this; @@ -563,7 +539,7 @@ * * @type string */ - builder: "bs5/dt-2.0.8", + builder: "bs5/dt-2.1.8", /** @@ -1033,6 +1009,15 @@ info: { container: 'dt-info' }, + layout: { + row: 'dt-layout-row', + cell: 'dt-layout-cell', + tableRow: 'dt-layout-table', + tableCell: '', + start: 'dt-layout-start', + end: 'dt-layout-end', + full: 'dt-layout-full' + }, length: { container: 'dt-length', select: 'dt-input' @@ -1081,7 +1066,8 @@ active: 'current', button: 'dt-paging-button', container: 'dt-paging', - disabled: 'disabled' + disabled: 'disabled', + nav: '' } } ); @@ -1156,7 +1142,7 @@ }; - var _isNumber = function ( d, decimalPoint, formatted ) { + var _isNumber = function ( d, decimalPoint, formatted, allowEmpty ) { var type = typeof d; var strType = type === 'string'; @@ -1167,7 +1153,7 @@ // If empty return immediately so there must be a number if it is a // formatted string (this stops the string "k", or "kr", etc being detected // as a formatted number for currency - if ( _empty( d ) ) { + if ( allowEmpty && _empty( d ) ) { return true; } @@ -1189,8 +1175,8 @@ }; // Is a string a number surrounded by HTML? - var _htmlNumeric = function ( d, decimalPoint, formatted ) { - if ( _empty( d ) ) { + var _htmlNumeric = function ( d, decimalPoint, formatted, allowEmpty ) { + if ( allowEmpty && _empty( d ) ) { return true; } @@ -1202,7 +1188,7 @@ var html = _isHtml( d ); return ! html ? null : - _isNumber( _stripHtml( d ), decimalPoint, formatted ) ? + _isNumber( _stripHtml( d ), decimalPoint, formatted, allowEmpty ) ? true : null; }; @@ -1244,7 +1230,7 @@ // is essential here if ( prop2 !== undefined ) { for ( ; i _max_str_len) { throw new Error('Exceeded max str len'); @@ -1340,8 +1330,11 @@ } // It is faster to just run `normalize` than it is to check if - // we need to with a regex! - var res = str.normalize("NFD"); + // we need to with a regex! (Check as it isn't available in old + // Safari) + var res = str.normalize + ? str.normalize("NFD") + : str; // Equally, here we check if a regex is needed or not return res.length !== str.length @@ -2264,6 +2257,21 @@ return a; } + /** + * Allow the result from a type detection function to be `true` while + * translating that into a string. Old type detection functions will + * return the type name if it passes. An obect store would be better, + * but not backwards compatible. + * + * @param {*} typeDetect Object or function for type detection + * @param {*} res Result from the type detection function + * @returns Type name or false + */ + function _typeResult (typeDetect, res) { + return res === true + ? typeDetect._name + : res; + } /** * Calculate the 'type' of a column @@ -2278,7 +2286,7 @@ var i, ien, j, jen, k, ken; var col, detectedType, cache; - // For each column, spin over the + // For each column, spin over the data type detection functions, seeing if one matches for ( i=0, ien=columns.length ; i col is set to and correct if needed - for (var i=0 ; i col is set to and correct if needed + for (var i=0 ; i 0 ? idx : null; + } + + // `:visible` on its own + return idx; } ); case 'name': @@ -9215,23 +9404,60 @@ } ); /** - * Set the jQuery or window object to be used by DataTables - * - * @param {*} module Library / container object - * @param {string} [type] Library or container type `lib`, `win` or `datetime`. - * If not provided, automatic detection is attempted. + * Set the libraries that DataTables uses, or the global objects. + * Note that the arguments can be either way around (legacy support) + * and the second is optional. See docs. */ - DataTable.use = function (module, type) { - if (type === 'lib' || module.fn) { + DataTable.use = function (arg1, arg2) { + // Reverse arguments for legacy support + var module = typeof arg1 === 'string' + ? arg2 + : arg1; + var type = typeof arg2 === 'string' + ? arg2 + : arg1; + + // Getter + if (module === undefined && typeof type === 'string') { + switch (type) { + case 'lib': + case 'jq': + return $; + + case 'win': + return window; + + case 'datetime': + return DataTable.DateTime; + + case 'luxon': + return __luxon; + + case 'moment': + return __moment; + + default: + return null; + } + } + + // Setter + if (type === 'lib' || type === 'jq' || (module && module.fn && module.fn.jquery)) { $ = module; } - else if (type == 'win' || module.document) { + else if (type == 'win' || (module && module.document)) { window = module; document = module.document; } - else if (type === 'datetime' || module.type === 'DateTime') { + else if (type === 'datetime' || (module && module.type === 'DateTime')) { DataTable.DateTime = module; } + else if (type === 'luxon' || (module && module.FixedOffsetZone)) { + __luxon = module; + } + else if (type === 'moment' || (module && module.isMoment)) { + __moment = module; + } } /** @@ -9487,7 +9713,7 @@ fn.call(this); } else { - this.on('init', function () { + this.on('init.dt.DT', function () { fn.call(this); }); } @@ -9640,7 +9866,7 @@ * @type string * @default Version number */ - DataTable.version = "2.0.8"; + DataTable.version = "2.1.8"; /** * Private data store, containing all of the settings objects that are @@ -10485,7 +10711,8 @@ first: 'First', last: 'Last', next: 'Next', - previous: 'Previous' + previous: 'Previous', + number: '' } }, @@ -10665,6 +10892,10 @@ }, + /** The initial data order is reversed when `desc` ordering */ + orderDescReverse: true, + + /** * This parameter allows you to have define the global filtering state at * initialisation time. As an object the `search` parameter must be @@ -10713,7 +10944,7 @@ * * `full_numbers` - 'First', 'Previous', 'Next' and 'Last' buttons, plus page numbers * * `first_last_numbers` - 'First' and 'Last' buttons, plus page numbers */ - "sPaginationType": "full_numbers", + "sPaginationType": "", /** @@ -10783,7 +11014,13 @@ /** * Caption value */ - "caption": null + "caption": null, + + + /** + * For server-side processing - use the data from the DOM for the first draw + */ + iDeferLoading: null }; _fnHungarianMap( DataTable.defaults ); @@ -11726,7 +11963,13 @@ captionNode: null, - colgroup: null + colgroup: null, + + /** Delay loading of data */ + deferLoading: null, + + /** Allow auto type detection */ + typeDetect: true }; /** @@ -11750,7 +11993,7 @@ }, full: function () { - return [ 'first', 'previous', 'next', 'last' ]; + return [ 'first', 'previous', 'next', 'last' ]; }, numbers: function () { @@ -11764,11 +12007,11 @@ full_numbers: function () { return [ 'first', 'previous', 'numbers', 'next', 'last' ]; }, - + first_last: function () { return ['first', 'last']; }, - + first_last_numbers: function () { return ['first', 'numbers', 'last']; }, @@ -11850,38 +12093,56 @@ * to make working with DataTables a little bit easier. */ - function __mldFnName(name) { - return name.replace(/[\W]/g, '_') - } - - // Common logic for moment, luxon or a date action - function __mld( dt, momentFn, luxonFn, dateFn, arg1 ) { - if (window.moment) { - return dt[momentFn]( arg1 ); + /** + * Common logic for moment, luxon or a date action. + * + * Happens after __mldObj, so don't need to call `resolveWindowsLibs` again + */ + function __mld( dtLib, momentFn, luxonFn, dateFn, arg1 ) { + if (__moment) { + return dtLib[momentFn]( arg1 ); } - else if (window.luxon) { - return dt[luxonFn]( arg1 ); + else if (__luxon) { + return dtLib[luxonFn]( arg1 ); } - return dateFn ? dt[dateFn]( arg1 ) : dt; + return dateFn ? dtLib[dateFn]( arg1 ) : dtLib; } var __mlWarning = false; + var __luxon; // Can be assigned in DateTeble.use() + var __moment; // Can be assigned in DateTeble.use() + + /** + * + */ + function resolveWindowLibs() { + if (window.luxon && ! __luxon) { + __luxon = window.luxon; + } + + if (window.moment && ! __moment) { + __moment = window.moment; + } + } + function __mldObj (d, format, locale) { var dt; - if (window.moment) { - dt = window.moment.utc( d, format, locale, true ); + resolveWindowLibs(); + + if (__moment) { + dt = __moment.utc( d, format, locale, true ); if (! dt.isValid()) { return null; } } - else if (window.luxon) { + else if (__luxon) { dt = format && typeof d === 'string' - ? window.luxon.DateTime.fromFormat( d, format ) - : window.luxon.DateTime.fromISO( d ); + ? __luxon.DateTime.fromFormat( d, format ) + : __luxon.DateTime.fromISO( d ); if (! dt.isValid) { return null; @@ -11926,11 +12187,11 @@ from = null; } - var typeName = 'datetime' + (to ? '-' + __mldFnName(to) : ''); + var typeName = 'datetime' + (to ? '-' + to : ''); // Add type detection and sorting specific to this date format - we need to be able to identify // date type columns as such, rather than as numbers in extensions. Hence the need for this. - if (! DataTable.ext.type.order[typeName]) { + if (! DataTable.ext.type.order[typeName + '-pre']) { DataTable.type(typeName, { detect: function (d) { // The renderer will give the value to type detect as the type! @@ -12029,7 +12290,7 @@ // Formatted date time detection - use by declaring the formats you are going to use DataTable.datetime = function ( format, locale ) { - var typeName = 'datetime-detect-' + __mldFnName(format); + var typeName = 'datetime-' + format; if (! locale) { locale = 'en'; @@ -12169,7 +12430,7 @@ return { className: _extTypes.className[name], detect: _extTypes.detect.find(function (fn) { - return fn.name === name; + return fn._name === name; }), order: { pre: _extTypes.order[name + '-pre'], @@ -12184,27 +12445,20 @@ var setProp = function(prop, propVal) { _extTypes[prop][name] = propVal; }; - var setDetect = function (fn) { - // Wrap to allow the function to return `true` rather than - // specifying the type name. - var cb = function (d, s) { - var ret = fn(d, s); + var setDetect = function (detect) { + // `detect` can be a function or an object - we set a name + // property for either - that is used for the detection + Object.defineProperty(detect, "_name", {value: name}); - return ret === true - ? name - : ret; - }; - Object.defineProperty(cb, "name", {value: name}); - - var idx = _extTypes.detect.findIndex(function (fn) { - return fn.name === name; + var idx = _extTypes.detect.findIndex(function (item) { + return item._name === name; }); if (idx === -1) { - _extTypes.detect.unshift(cb); + _extTypes.detect.unshift(detect); } else { - _extTypes.detect.splice(idx, 1, cb); + _extTypes.detect.splice(idx, 1, detect); } }; var setOrder = function (obj) { @@ -12260,10 +12514,23 @@ // Get a list of types DataTable.types = function () { return _extTypes.detect.map(function (fn) { - return fn.name; + return fn._name; }); }; + var __diacriticSort = function (a, b) { + a = a !== null && a !== undefined ? a.toString().toLowerCase() : ''; + b = b !== null && b !== undefined ? b.toString().toLowerCase() : ''; + + // Checked for `navigator.languages` support in `oneOf` so this code can't execute in old + // Safari and thus can disable this check + // eslint-disable-next-line compat/compat + return a.localeCompare(b, navigator.languages[0] || navigator.language, { + numeric: true, + ignorePunctuation: true, + }); + } + // // Built in data types // @@ -12276,7 +12543,7 @@ pre: function ( a ) { // This is a little complex, but faster than always calling toString, // http://jsperf.com/tostring-v-check - return _empty(a) ? + return _empty(a) && typeof a !== 'boolean' ? '' : typeof a === 'string' ? a.toLowerCase() : @@ -12288,11 +12555,38 @@ search: _filterString(false, true) }); + DataTable.type('string-utf8', { + detect: { + allOf: function ( d ) { + return true; + }, + oneOf: function ( d ) { + // At least one data point must contain a non-ASCII character + // This line will also check if navigator.languages is supported or not. If not (Safari 10.0-) + // this data type won't be supported. + // eslint-disable-next-line compat/compat + return ! _empty( d ) && navigator.languages && typeof d === 'string' && d.match(/[^\x00-\x7F]/); + } + }, + order: { + asc: __diacriticSort, + desc: function (a, b) { + return __diacriticSort(a, b) * -1; + } + }, + search: _filterString(false, true) + }); + DataTable.type('html', { - detect: function ( d ) { - return _empty( d ) || (typeof d === 'string' && d.indexOf('<') !== -1) ? - 'html' : null; + detect: { + allOf: function ( d ) { + return _empty( d ) || (typeof d === 'string' && d.indexOf('<') !== -1); + }, + oneOf: function ( d ) { + // At least one data point must contain a `<` + return ! _empty( d ) && typeof d === 'string' && d.indexOf('<') !== -1; + } }, order: { pre: function ( a ) { @@ -12309,16 +12603,21 @@ DataTable.type('date', { className: 'dt-type-date', - detect: function ( d ) - { - // V8 tries _very_ hard to make a string passed into `Date.parse()` - // valid, so we need to use a regex to restrict date formats. Use a - // plug-in for anything other than ISO8601 style strings - if ( d && !(d instanceof Date) && ! _re_date.test(d) ) { - return null; + detect: { + allOf: function ( d ) { + // V8 tries _very_ hard to make a string passed into `Date.parse()` + // valid, so we need to use a regex to restrict date formats. Use a + // plug-in for anything other than ISO8601 style strings + if ( d && !(d instanceof Date) && ! _re_date.test(d) ) { + return null; + } + var parsed = Date.parse(d); + return (parsed !== null && !isNaN(parsed)) || _empty(d); + }, + oneOf: function ( d ) { + // At least one entry must be a date or a string with a date + return (d instanceof Date) || (typeof d === 'string' && _re_date.test(d)); } - var parsed = Date.parse(d); - return (parsed !== null && !isNaN(parsed)) || _empty(d) ? 'date' : null; }, order: { pre: function ( d ) { @@ -12331,10 +12630,16 @@ DataTable.type('html-num-fmt', { className: 'dt-type-numeric', - detect: function ( d, settings ) - { - var decimal = settings.oLanguage.sDecimal; - return _htmlNumeric( d, decimal, true ) ? 'html-num-fmt' : null; + detect: { + allOf: function ( d, settings ) { + var decimal = settings.oLanguage.sDecimal; + return _htmlNumeric( d, decimal, true, false ); + }, + oneOf: function (d, settings) { + // At least one data point must contain a numeric value + var decimal = settings.oLanguage.sDecimal; + return _htmlNumeric( d, decimal, true, false ); + } }, order: { pre: function ( d, s ) { @@ -12348,10 +12653,16 @@ DataTable.type('html-num', { className: 'dt-type-numeric', - detect: function ( d, settings ) - { - var decimal = settings.oLanguage.sDecimal; - return _htmlNumeric( d, decimal ) ? 'html-num' : null; + detect: { + allOf: function ( d, settings ) { + var decimal = settings.oLanguage.sDecimal; + return _htmlNumeric( d, decimal, false, true ); + }, + oneOf: function (d, settings) { + // At least one data point must contain a numeric value + var decimal = settings.oLanguage.sDecimal; + return _htmlNumeric( d, decimal, false, false ); + } }, order: { pre: function ( d, s ) { @@ -12365,10 +12676,16 @@ DataTable.type('num-fmt', { className: 'dt-type-numeric', - detect: function ( d, settings ) - { - var decimal = settings.oLanguage.sDecimal; - return _isNumber( d, decimal, true ) ? 'num-fmt' : null; + detect: { + allOf: function ( d, settings ) { + var decimal = settings.oLanguage.sDecimal; + return _isNumber( d, decimal, true, true ); + }, + oneOf: function (d, settings) { + // At least one data point must contain a numeric value + var decimal = settings.oLanguage.sDecimal; + return _isNumber( d, decimal, true, false ); + } }, order: { pre: function ( d, s ) { @@ -12381,10 +12698,16 @@ DataTable.type('num', { className: 'dt-type-numeric', - detect: function ( d, settings ) - { - var decimal = settings.oLanguage.sDecimal; - return _isNumber( d, decimal ) ? 'num' : null; + detect: { + allOf: function ( d, settings ) { + var decimal = settings.oLanguage.sDecimal; + return _isNumber( d, decimal, false, true ); + }, + oneOf: function (d, settings) { + // At least one data point must contain a numeric value + var decimal = settings.oLanguage.sDecimal; + return _isNumber( d, decimal, false, false ); + } }, order: { pre: function (d, s) { @@ -12468,11 +12791,18 @@ // `DT` namespace will allow the event to be removed automatically // on destroy, while the `dt` namespaced event is the one we are // listening for - $(settings.nTable).on( 'order.dt.DT', function ( e, ctx, sorting ) { + $(settings.nTable).on( 'order.dt.DT column-visibility.dt.DT', function ( e, ctx ) { if ( settings !== ctx ) { // need to check this this is the host return; // table, not a nested one } + var sorting = ctx.sortDetails; + + if (! sorting) { + return; + } + + var i; var orderClasses = classes.order; var columns = ctx.api.columns( cell ); var col = settings.aoColumns[columns.flatten()[0]]; @@ -12480,9 +12810,7 @@ var ariaType = ''; var indexes = columns.indexes(); var sortDirs = columns.orderable(true).flatten(); - var orderedColumns = ',' + sorting.map( function (val) { - return val.col; - } ).join(',') + ','; + var orderedColumns = _pluck(sorting, 'col'); cell .removeClass( @@ -12492,10 +12820,18 @@ .toggleClass( orderClasses.none, ! orderable ) .toggleClass( orderClasses.canAsc, orderable && sortDirs.includes('asc') ) .toggleClass( orderClasses.canDesc, orderable && sortDirs.includes('desc') ); - - var sortIdx = orderedColumns.indexOf( ',' + indexes.toArray().join(',') + ',' ); - if ( sortIdx !== -1 ) { + // Determine if all of the columns that this cell covers are included in the + // current ordering + var isOrdering = true; + + for (i=0; i') - .addClass('dt-layout-row') + .attr('id', items.id || null) + .addClass(items.className || classes.row) .appendTo( container ); $.each( items, function (key, val) { - var klass = ! val.table ? - 'dt-'+key+' ' : - ''; + if (key === 'id' || key === 'className') { + return; + } + + var klass = ''; if (val.table) { - row.addClass('dt-layout-table'); + row.addClass(classes.tableRow); + klass += classes.tableCell + ' '; + } + + if (key === 'start') { + klass += classes.start; + } + else if (key === 'end') { + klass += classes.end; + } + else { + klass += classes.full; } $('
') .attr({ id: val.id || null, - "class": 'dt-layout-cell '+klass+(val.className || '') + "class": val.className + ? val.className + : classes.cell + ' ' + klass }) .append( val.contents ) .appendTo( row ); @@ -12576,6 +12941,25 @@ } }; + function _divProp(el, prop, val) { + if (val) { + el[prop] = val; + } + } + + DataTable.feature.register( 'div', function ( settings, opts ) { + var n = $('
')[0]; + + if (opts) { + _divProp(n, 'className', opts.className); + _divProp(n, 'id', opts.id); + _divProp(n, 'innerHTML', opts.html); + _divProp(n, 'textContent', opts.text); + } + + return n; + } ); + DataTable.feature.register( 'info', function ( settings, opts ) { // For compatibility with the legacy `info` top level option if (! settings.oFeatures.bInfo) { @@ -12675,6 +13059,7 @@ opts = $.extend({ placeholder: language.sSearchPlaceholder, + processing: false, text: language.sSearch }, opts); @@ -12718,13 +13103,15 @@ /* Now do the filter */ if ( val != previousSearch.search ) { - previousSearch.search = val; - - _fnFilterComplete( settings, previousSearch ); - - // Need to redraw, without resorting - settings._iDisplayStart = 0; - _fnDraw( settings ); + _fnProcessingRun(settings, opts.processing, function () { + previousSearch.search = val; + + _fnFilterComplete( settings, previousSearch ); + + // Need to redraw, without resorting + settings._iDisplayStart = 0; + _fnDraw( settings ); + }); } }; @@ -12782,17 +13169,21 @@ opts = $.extend({ buttons: DataTable.ext.pager.numbers_length, type: settings.sPaginationType, - boundaryNumbers: true + boundaryNumbers: true, + firstLast: true, + previousNext: true, + numbers: true }, opts); - // To be removed in 2.1 - if (opts.numbers) { - opts.buttons = opts.numbers; - } - - var host = $('
').addClass( settings.oClasses.paging.container + ' paging_' + opts.type ); + var host = $('
') + .addClass(settings.oClasses.paging.container + (opts.type ? ' paging_' + opts.type : '')) + .append( + $('