diff --git a/.env.template b/.env.template index bf025d8d..a5e7f458 100644 --- a/.env.template +++ b/.env.template @@ -324,6 +324,14 @@ ## Set to the string "none" (without quotes), to disable any headers and just use the remote IP # IP_HEADER=X-Real-IP +## Which addresses the header above is accepted from, defaults to "local". +## Anyone able to reach Vaultwarden can set the header, and the client IP is used for the login and +## admin rate limits, so it is only trusted when the request comes from a proxy listed here. +## "local" accepts it from any non global address, which covers a reverse proxy running on the same +## host or container network. Use "all" to accept it from anywhere, or list the addresses of your +## proxy as IPs and CIDR ranges if it connects from a public address. +# IP_HEADER_TRUSTED_PROXIES=local + ## Icon service ## The predefined icon services are: internal, bitwarden, duckduckgo, google. ## To specify a custom icon service, set a URL template with exactly one instance of `{}`, @@ -461,6 +469,13 @@ ## Note that this applies to both the login and the 2FA, so it's recommended to allow a burst size of at least 2. # LOGIN_RATELIMIT_MAX_BURST=10 +## Number of seconds, on average, between requests from the same IP address to one of the rate limited +## unauthenticated endpoints, like the password hint, the account recovery mails or accessing a Send. +# UNAUTHENTICATED_RATELIMIT_SECONDS=60 +## Allow a burst of requests of up to this size, while maintaining the average indicated by `UNAUTHENTICATED_RATELIMIT_SECONDS`. +## This budget is shared between all of those endpoints, so it is more lenient than the login one. +# UNAUTHENTICATED_RATELIMIT_MAX_BURST=50 + ## BETA FEATURE: Groups ## Controls whether group support is enabled for organizations ## This setting applies to organizations. diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c9e8442e..31a04012 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -62,7 +62,7 @@ jobs: # Checkout the repo - name: "Checkout" - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false fetch-depth: 0 diff --git a/.github/workflows/check-templates.yml b/.github/workflows/check-templates.yml index d9e139db..da4d90fd 100644 --- a/.github/workflows/check-templates.yml +++ b/.github/workflows/check-templates.yml @@ -20,7 +20,7 @@ jobs: steps: # Checkout the repo - name: "Checkout" - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false # End Checkout the repo diff --git a/.github/workflows/hadolint.yml b/.github/workflows/hadolint.yml index 60fafe9e..17151922 100644 --- a/.github/workflows/hadolint.yml +++ b/.github/workflows/hadolint.yml @@ -32,7 +32,7 @@ jobs: # Checkout the repo - name: Checkout - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false # End Checkout the repo diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index cbd32451..0efd21db 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -77,7 +77,7 @@ jobs: # Checkout the repo - name: Checkout - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 # We need fetch-depth of 0 so we also get all the tag metadata with: persist-credentials: false @@ -106,7 +106,7 @@ jobs: # Login to Docker Hub - name: Login to Docker Hub - uses: docker/login-action@af1e73f918a031802d376d3c8bbc3fe56130a9b0 # v4.4.0 + uses: docker/login-action@abd2ef45e78c5afb21d64d4ca52ee8550d9572c7 # v4.5.1 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} @@ -121,7 +121,7 @@ jobs: # Login to GitHub Container Registry - name: Login to GitHub Container Registry - uses: docker/login-action@af1e73f918a031802d376d3c8bbc3fe56130a9b0 # v4.4.0 + uses: docker/login-action@abd2ef45e78c5afb21d64d4ca52ee8550d9572c7 # v4.5.1 with: registry: ghcr.io username: ${{ github.repository_owner }} @@ -137,7 +137,7 @@ jobs: # Login to Quay.io - name: Login to Quay.io - uses: docker/login-action@af1e73f918a031802d376d3c8bbc3fe56130a9b0 # v4.4.0 + uses: docker/login-action@abd2ef45e78c5afb21d64d4ca52ee8550d9572c7 # v4.5.1 with: registry: quay.io username: ${{ secrets.QUAY_USERNAME }} @@ -237,7 +237,7 @@ jobs: # Upload artifacts to Github Actions and Attest the binaries - name: Attest binaries - uses: actions/attest@a1948c3f048ba23858d222213b7c278aabede763 # v4.1.1 + uses: actions/attest@f7c74d28b9d84cb8768d0b8ca14a4bac6ef463e6 # v4.2.0 with: subject-path: vaultwarden-${{ env.NORMALIZED_ARCH }} @@ -272,7 +272,7 @@ jobs: # Login to Docker Hub - name: Login to Docker Hub - uses: docker/login-action@af1e73f918a031802d376d3c8bbc3fe56130a9b0 # v4.4.0 + uses: docker/login-action@abd2ef45e78c5afb21d64d4ca52ee8550d9572c7 # v4.5.1 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} @@ -287,7 +287,7 @@ jobs: # Login to GitHub Container Registry - name: Login to GitHub Container Registry - uses: docker/login-action@af1e73f918a031802d376d3c8bbc3fe56130a9b0 # v4.4.0 + uses: docker/login-action@abd2ef45e78c5afb21d64d4ca52ee8550d9572c7 # v4.5.1 with: registry: ghcr.io username: ${{ github.repository_owner }} @@ -303,7 +303,7 @@ jobs: # Login to Quay.io - name: Login to Quay.io - uses: docker/login-action@af1e73f918a031802d376d3c8bbc3fe56130a9b0 # v4.4.0 + uses: docker/login-action@abd2ef45e78c5afb21d64d4ca52ee8550d9572c7 # v4.5.1 with: registry: quay.io username: ${{ secrets.QUAY_USERNAME }} @@ -365,7 +365,7 @@ jobs: # Attest container images - name: Attest - docker.io - ${{ matrix.base_image }} if: ${{ vars.DOCKERHUB_REPO != '' && env.DIGEST_SHA != ''}} - uses: actions/attest@a1948c3f048ba23858d222213b7c278aabede763 # v4.1.1 + uses: actions/attest@f7c74d28b9d84cb8768d0b8ca14a4bac6ef463e6 # v4.2.0 with: subject-name: ${{ vars.DOCKERHUB_REPO }} subject-digest: ${{ env.DIGEST_SHA }} @@ -373,7 +373,7 @@ jobs: - name: Attest - ghcr.io - ${{ matrix.base_image }} if: ${{ vars.GHCR_REPO != '' && env.DIGEST_SHA != ''}} - uses: actions/attest@a1948c3f048ba23858d222213b7c278aabede763 # v4.1.1 + uses: actions/attest@f7c74d28b9d84cb8768d0b8ca14a4bac6ef463e6 # v4.2.0 with: subject-name: ${{ vars.GHCR_REPO }} subject-digest: ${{ env.DIGEST_SHA }} @@ -381,7 +381,7 @@ jobs: - name: Attest - quay.io - ${{ matrix.base_image }} if: ${{ vars.QUAY_REPO != '' && env.DIGEST_SHA != ''}} - uses: actions/attest@a1948c3f048ba23858d222213b7c278aabede763 # v4.1.1 + uses: actions/attest@f7c74d28b9d84cb8768d0b8ca14a4bac6ef463e6 # v4.2.0 with: subject-name: ${{ vars.QUAY_REPO }} subject-digest: ${{ env.DIGEST_SHA }} diff --git a/.github/workflows/trivy.yml b/.github/workflows/trivy.yml index eaf89350..c1f42c56 100644 --- a/.github/workflows/trivy.yml +++ b/.github/workflows/trivy.yml @@ -33,7 +33,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false @@ -50,6 +50,6 @@ jobs: severity: CRITICAL,HIGH - name: Upload Trivy scan results to GitHub Security tab - uses: github/codeql-action/upload-sarif@99df26d4f13ea111d4ec1a7dddef6063f76b97e9 # v4.37.0 + uses: github/codeql-action/upload-sarif@e4fba868fa4b1b91e1fdab776edc8cfbe6e9fb81 # v4.37.3 with: sarif_file: 'trivy-results.sarif' diff --git a/.github/workflows/typos.yml b/.github/workflows/typos.yml index e906b5bd..7c345e0a 100644 --- a/.github/workflows/typos.yml +++ b/.github/workflows/typos.yml @@ -16,7 +16,7 @@ jobs: steps: # Checkout the repo - name: Checkout - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false # End Checkout the repo diff --git a/.github/workflows/zizmor.yml b/.github/workflows/zizmor.yml index b8f66cbe..72810c67 100644 --- a/.github/workflows/zizmor.yml +++ b/.github/workflows/zizmor.yml @@ -19,12 +19,12 @@ jobs: security-events: write # To write the security report steps: - name: Checkout repository - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: Run zizmor - uses: zizmorcore/zizmor-action@192e21d79ab29983730a13d1382995c2307fbcaa # v0.5.7 + uses: zizmorcore/zizmor-action@6fc4b006235f201fdab3722e17240ab420d580e5 # v0.6.1 with: # intentionally not scanning the entire repository, # since it contains integration tests. diff --git a/Cargo.lock b/Cargo.lock index 0715098c..6b20797b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,17 +8,6 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" -[[package]] -name = "aes" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" -dependencies = [ - "cfg-if", - "cipher", - "cpufeatures 0.2.17", -] - [[package]] name = "ahash" version = "0.8.12" @@ -72,9 +61,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.103" +version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a4385e2e34eb35d6b3efe798b9eb88096925d87726c0798709bf56d9ed84af3" +checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470" [[package]] name = "arc-swap" @@ -121,7 +110,7 @@ checksum = "965c2d33e53cb6b267e148a4cb0760bc01f4904c1cd4bb4002a085bb016d1490" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", "synstructure", ] @@ -133,7 +122,7 @@ checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -311,7 +300,7 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -322,13 +311,13 @@ checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" [[package]] name = "async-trait" -version = "0.1.89" +version = "0.1.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +checksum = "ae36dc4177970ef04fde5178d3e2429882def40e57a451f919c098f72baa6cec" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 3.0.3", ] [[package]] @@ -360,9 +349,9 @@ checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" [[package]] name = "aws-config" -version = "1.8.18" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e33f815b73a3899c03b380d543532e5865f230dce9678d108dc10732a8682275" +checksum = "701418aa459dac33e50a0f8e818e5662a16bc018a6ac7423659b70f3799d67a8" dependencies = [ "aws-credential-types", "aws-runtime", @@ -381,7 +370,7 @@ dependencies = [ "fastrand", "hex", "http 1.4.2", - "sha1 0.10.6", + "sha1 0.10.7", "time", "tokio", "tracing", @@ -391,9 +380,9 @@ dependencies = [ [[package]] name = "aws-credential-types" -version = "1.2.14" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f20799b373a1be121fe3005fba0c2090af9411573878f224df44b42727fcaf7" +checksum = "e93964ffdaf57857f544be3666a5f57570bb699e934700f11b49708f61bb556e" dependencies = [ "aws-smithy-async", "aws-smithy-runtime-api", @@ -403,9 +392,9 @@ dependencies = [ [[package]] name = "aws-runtime" -version = "1.7.5" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c9b9de216a988dd54b754a82a7660cfe14cee4f6782ae4524470972fa0ccb39" +checksum = "a6b50a43f3ccdf331521c6d6c68b7cc9668b6e09d439ebda9569df5722324d76" dependencies = [ "aws-credential-types", "aws-sigv4", @@ -419,7 +408,7 @@ dependencies = [ "bytes-utils", "fastrand", "http 1.4.2", - "http-body 1.0.1", + "http-body 1.1.0", "percent-encoding", "pin-project-lite", "tracing", @@ -428,9 +417,9 @@ dependencies = [ [[package]] name = "aws-sdk-sso" -version = "1.102.0" +version = "1.104.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c82b3ac19f1431854f7ace3a7531674633e286bfdde21976893bfee36fd493b" +checksum = "b53416d16c278234845392e38d93bd4481d2f09daa0f005a2277f0aa91f59c22" dependencies = [ "arc-swap", "aws-credential-types", @@ -441,6 +430,7 @@ dependencies = [ "aws-smithy-observability", "aws-smithy-runtime", "aws-smithy-runtime-api", + "aws-smithy-schema", "aws-smithy-types", "aws-types", "bytes", @@ -453,9 +443,9 @@ dependencies = [ [[package]] name = "aws-sdk-ssooidc" -version = "1.104.0" +version = "1.106.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "321000d2b4c5519ee573f73167f612efd7329322d9b26969ad1979f0427f1913" +checksum = "cc9b706c3305ed0285d5b1b696c747aa34950f830fb03e3e6c76890f99b9f188" dependencies = [ "arc-swap", "aws-credential-types", @@ -466,6 +456,7 @@ dependencies = [ "aws-smithy-observability", "aws-smithy-runtime", "aws-smithy-runtime-api", + "aws-smithy-schema", "aws-smithy-types", "aws-types", "bytes", @@ -478,9 +469,9 @@ dependencies = [ [[package]] name = "aws-sdk-sts" -version = "1.107.0" +version = "1.109.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d0d328ba962af23ecfa3c9f23b98d3d35e325fa218d7f13d17a6bf522f8a560" +checksum = "32d214cdfa5bbe17f117e76a7643fadf32a5234fb597322ef8b1fb4b2f17dbbd" dependencies = [ "arc-swap", "aws-credential-types", @@ -492,6 +483,7 @@ dependencies = [ "aws-smithy-query", "aws-smithy-runtime", "aws-smithy-runtime-api", + "aws-smithy-schema", "aws-smithy-types", "aws-smithy-xml", "aws-types", @@ -504,9 +496,9 @@ dependencies = [ [[package]] name = "aws-sigv4" -version = "1.4.5" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bae38512beae0ffee7010fc24e7a8a123c53efdfef42a61e80fda4882418dc71" +checksum = "723c2234ad7511ceef63eab016b7ba6ff7c55590fefb96fa8467af014a07309f" dependencies = [ "aws-credential-types", "aws-smithy-http", @@ -537,9 +529,9 @@ dependencies = [ [[package]] name = "aws-smithy-http" -version = "0.63.6" +version = "0.64.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba1ab2dc1c2c3749ead27180d333c42f11be8b0e934058fb4b2258ee8dbe5231" +checksum = "37843d9add67c3aff5856f409c6dc315d3cdff60f9c0cb5b670dab1e9920306d" dependencies = [ "aws-smithy-runtime-api", "aws-smithy-types", @@ -548,7 +540,7 @@ dependencies = [ "futures-core", "futures-util", "http 1.4.2", - "http-body 1.0.1", + "http-body 1.1.0", "http-body-util", "percent-encoding", "pin-project-lite", @@ -558,9 +550,9 @@ dependencies = [ [[package]] name = "aws-smithy-json" -version = "0.62.7" +version = "0.63.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "701a947f4797e52a911e114a898667c746c39feea467bbd1abd7b3721f702ffa" +checksum = "3dc65a121adb4b33729919fcfa14fa36fb33c1555a8f06bb0e2188dbfdc1d9ef" dependencies = [ "aws-smithy-runtime-api", "aws-smithy-schema", @@ -569,28 +561,31 @@ dependencies = [ [[package]] name = "aws-smithy-observability" -version = "0.2.6" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a06c2315d173edbf1920da8ba3a7189695827002e4c0fc961973ab1c54abca9c" +checksum = "8e86338c869539a581bf161247762a6e87f92c5c075060057b5ed6d06632ed0c" dependencies = [ "aws-smithy-runtime-api", ] [[package]] name = "aws-smithy-query" -version = "0.60.15" +version = "0.62.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a56d79744fb3edb5d722ef79d86081e121d3b9422cb209eb03aea6aa4f21ebd" +checksum = "512346c7212ab7436df2d77a16d976a468ae44a418835511d2a69269810aaf62" dependencies = [ + "aws-smithy-runtime-api", + "aws-smithy-schema", "aws-smithy-types", + "aws-smithy-xml", "urlencoding", ] [[package]] name = "aws-smithy-runtime" -version = "1.11.3" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8e6f5caf6fea86f8c2206541ab5857cfcda9013426cdbe8fa0098b9e2d32182" +checksum = "07505b34e8f4b3591a4fa69e9792b52289b95488dbbc68c3c0075b7bedb245e1" dependencies = [ "aws-smithy-async", "aws-smithy-http", @@ -603,7 +598,7 @@ dependencies = [ "http 0.2.12", "http 1.4.2", "http-body 0.4.6", - "http-body 1.0.1", + "http-body 1.1.0", "http-body-util", "pin-project-lite", "pin-utils", @@ -613,9 +608,9 @@ dependencies = [ [[package]] name = "aws-smithy-runtime-api" -version = "1.13.0" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22ed1ebe6e0a95ea84570225f5a8208dec4b8f77e61a9b0d6f51773fcb4612f0" +checksum = "3b98f2e1fd67ec06618f9c291e5e495a468e60519e44c9c1979cd0521f3affdb" dependencies = [ "aws-smithy-async", "aws-smithy-runtime-api-macros", @@ -637,14 +632,14 @@ checksum = "221eaa237ddf1ca79b60d1372aad77e47f9c0ea5b3ce5099da8c61d027dc77b3" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] name = "aws-smithy-schema" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7442cb268338f0eb8278140a107c046756aa01093d8ef5e99628d34ae09c94f5" +checksum = "7d56e0a4e53127a632224e43633b0fe045fa9e1e3cfc68b9830f1115e103f910" dependencies = [ "aws-smithy-runtime-api", "aws-smithy-types", @@ -663,7 +658,7 @@ dependencies = [ "http 0.2.12", "http 1.4.2", "http-body 0.4.6", - "http-body 1.0.1", + "http-body 1.1.0", "http-body-util", "itoa", "num-integer", @@ -676,18 +671,21 @@ dependencies = [ [[package]] name = "aws-smithy-xml" -version = "0.60.15" +version = "0.62.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ce02add1aa3677d022f8adf81dcbe3046a95f17a1b1e8979c145cd21d3d22b3" +checksum = "ce84f71c72fee2cbbadde6e7d082f5fb466e3a84733855295fa7aafd1b31b7d8" dependencies = [ + "aws-smithy-runtime-api", + "aws-smithy-schema", + "aws-smithy-types", "xmlparser", ] [[package]] name = "aws-types" -version = "1.3.16" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d16bf10b03a3c01e6b3b7d47cd964e873ffe9e7d4e80fad16bd4c077cb068531" +checksum = "eec1cd5469f328c782dc3e33d4153cf118a54e33cbb3356d60d16f89883e1f94" dependencies = [ "aws-credential-types", "aws-smithy-async", @@ -770,9 +768,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.13.0" +version = "2.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" +checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da" [[package]] name = "blake2" @@ -801,15 +799,6 @@ dependencies = [ "hybrid-array", ] -[[package]] -name = "block-padding" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" -dependencies = [ - "generic-array", -] - [[package]] name = "blocking" version = "1.6.2" @@ -861,9 +850,9 @@ checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" [[package]] name = "bytemuck" -version = "1.25.0" +version = "1.25.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" +checksum = "95832e849adfb21180ccb6826a99da14e5d266ae5c2e668e1602cf234f153797" [[package]] name = "byteorder" @@ -898,7 +887,7 @@ dependencies = [ "cached_proc_macro_types", "hashbrown 0.16.1", "parking_lot", - "thiserror 2.0.18", + "thiserror 2.0.19", "tokio", "web-time", ] @@ -912,7 +901,7 @@ dependencies = [ "darling 0.20.11", "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -921,20 +910,11 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26cf465651fa6ad902a2d327ba60c3a6bc61c6a2f4ad70d091cf20dfda0074ef" -[[package]] -name = "cbc" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6" -dependencies = [ - "cipher", -] - [[package]] name = "cc" -version = "1.2.66" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5d6cac793997bd970000024b2934968efe83b382de4fdcf4fcb46b6ee4ad996" +checksum = "5add81bb678e6cb321aff7fa0dc7689ad82b112dbc032cea19f91d6b8e3582b9" dependencies = [ "find-msvc-tools", "jobserver", @@ -983,16 +963,6 @@ dependencies = [ "phf 0.12.1", ] -[[package]] -name = "cipher" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" -dependencies = [ - "crypto-common 0.1.6", - "inout", -] - [[package]] name = "cmov" version = "0.5.4" @@ -1287,7 +1257,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -1331,7 +1301,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn", + "syn 2.0.119", ] [[package]] @@ -1345,7 +1315,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn", + "syn 2.0.119", ] [[package]] @@ -1358,7 +1328,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn", + "syn 2.0.119", ] [[package]] @@ -1369,7 +1339,7 @@ checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core 0.20.11", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -1380,7 +1350,7 @@ checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" dependencies = [ "darling_core 0.21.3", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -1391,7 +1361,7 @@ checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" dependencies = [ "darling_core 0.23.0", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -1439,7 +1409,7 @@ dependencies = [ "defmt-parser", "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -1448,7 +1418,7 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e" dependencies = [ - "thiserror 2.0.18", + "thiserror 2.0.19", ] [[package]] @@ -1503,7 +1473,7 @@ dependencies = [ "darling 0.20.11", "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -1513,7 +1483,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" dependencies = [ "derive_builder_core", - "syn", + "syn 2.0.119", ] [[package]] @@ -1535,7 +1505,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn", + "syn 2.0.119", "unicode-xid", ] @@ -1565,21 +1535,21 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b035a542cf7abf01f2e3c4d5a7acbaebfefe120ae4efc7bde3df98186e4b8af7" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "proc-macro2", "proc-macro2-diagnostics", "quote", - "syn", + "syn 2.0.119", ] [[package]] name = "diesel" -version = "2.3.10" +version = "2.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29fe29a87fb84c631ffb3ba21798c4b1f3a964701ba78f0dce4bf8668562ec88" +checksum = "e54d1f576cd3a3460f212a4615fd12ce1b6303c095b79a44449ffbe627753dc1" dependencies = [ "bigdecimal", - "bitflags 2.13.0", + "bitflags 2.13.1", "byteorder", "chrono", "diesel_derives", @@ -1606,7 +1576,7 @@ checksum = "4c9c687e77914afc18b1e797d523ace0e5f08dc7805285bcdabd8646ea8d4de7" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -1619,7 +1589,7 @@ dependencies = [ "dsl_auto_type", "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -1639,7 +1609,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe2444076b48641147115697648dc743c2c00b61adade0f01ce67133c7babe8c" dependencies = [ - "syn", + "syn 2.0.119", ] [[package]] @@ -1674,7 +1644,7 @@ checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -1718,7 +1688,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -1865,9 +1835,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" +checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223" [[package]] name = "fern" @@ -1965,9 +1935,9 @@ dependencies = [ [[package]] name = "futures" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" +checksum = "a88cf1f829d945f548cf8fec32c61b1f202b6d93b45848602fc02af4b12ad218" dependencies = [ "futures-channel", "futures-core", @@ -1980,9 +1950,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +checksum = "262590f4fe6afeb0bc83be1daa64e52657fe185690a958af7f3ad0e92085c5ae" dependencies = [ "futures-core", "futures-sink", @@ -1990,15 +1960,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" +checksum = "2cd50c473c80f6d7c3670a752354b8e569b1a7cbfdc0419ec88e5edad85e0dc7" [[package]] name = "futures-executor" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" +checksum = "6754879cc9f2c66f88c6e5c35344bb0bdb0708b0352b1201815667c7eabc7458" dependencies = [ "futures-core", "futures-task", @@ -2007,9 +1977,9 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" +checksum = "4577ecaa3c4f96589d473f679a71b596316f6641bc350038b962a5daf0085d7a" [[package]] name = "futures-lite" @@ -2026,26 +1996,26 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +checksum = "2d6d3cde68c518367be28956066ddfef33813991b77a55005a69dae04bf3b10b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] name = "futures-sink" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" +checksum = "e34418ac499d6305c2fb5ad0ed2f6ac998c5f8ca209b4510f7f94242c647e307" [[package]] name = "futures-task" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" +checksum = "b231ed28831efb4a61a08580c4bc233ec56bc009f4cd8f52da2c3cb97df0c109" [[package]] name = "futures-timer" @@ -2055,9 +2025,9 @@ checksum = "af43fadb8a98512d547e37b4e92e0ced13e205c061b87b4623eff01d918d6968" [[package]] name = "futures-util" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +checksum = "a77a90a256fce34da66415271e30f94ee91c57b04b8a2c042d9cf3220179deaa" dependencies = [ "futures-channel", "futures-core", @@ -2135,9 +2105,9 @@ dependencies = [ [[package]] name = "glob" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" +checksum = "e4eba85ea1d0a966a983acd07deee566e67395d2d96b6fb39e62b5a833f1eb0b" [[package]] name = "gloo-timers" @@ -2168,7 +2138,7 @@ dependencies = [ "parking_lot", "portable-atomic", "quanta", - "rand 0.9.4", + "rand 0.9.5", "smallvec", "spinning_top", "web-time", @@ -2230,9 +2200,9 @@ dependencies = [ [[package]] name = "handlebars" -version = "6.4.2" +version = "6.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26569a2763497b7bd3fbd19374b774ea6038c5293678771259cd534d49740ff" +checksum = "4633d16a2350341713c379d6d06a4b9e1845329386026a49ce4fd09c2f3b16f6" dependencies = [ "derive_builder", "log", @@ -2241,7 +2211,7 @@ dependencies = [ "pest_derive", "serde", "serde_json", - "thiserror 2.0.18", + "thiserror 2.0.19", "walkdir", ] @@ -2313,7 +2283,7 @@ dependencies = [ "ipnet", "jni", "rand 0.10.2", - "thiserror 2.0.18", + "thiserror 2.0.19", "tinyvec", "tokio", "tracing", @@ -2334,7 +2304,7 @@ dependencies = [ "prefix-trie", "rand 0.10.2", "ring", - "thiserror 2.0.18", + "thiserror 2.0.19", "tinyvec", "tracing", "url", @@ -2361,7 +2331,7 @@ dependencies = [ "resolv-conf", "smallvec", "system-configuration", - "thiserror 2.0.18", + "thiserror 2.0.19", "tokio", "tracing", ] @@ -2447,9 +2417,9 @@ dependencies = [ [[package]] name = "http-body" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +checksum = "ca2a8f2913ee65f60facd6a5905613afaa448497a0230cc41ce022d93290bc2c" dependencies = [ "bytes", "http 1.4.2", @@ -2457,14 +2427,14 @@ dependencies = [ [[package]] name = "http-body-util" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +checksum = "e9f41fd6a08e4d4ec69df65976da761afd5ad5e58a9d4acb46bd1c953a9e3ff2" dependencies = [ "bytes", "futures-core", "http 1.4.2", - "http-body 1.0.1", + "http-body 1.1.0", "pin-project-lite", ] @@ -2514,9 +2484,9 @@ dependencies = [ [[package]] name = "hyper" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55281c53a1894c864990125767da440a4e630446785086f52523b20033b74498" +checksum = "d22053281f852e11534f5198498373cbb59295120a20771d90f7ed1897490a72" dependencies = [ "atomic-waker", "bytes", @@ -2524,7 +2494,7 @@ dependencies = [ "futures-core", "h2", "http 1.4.2", - "http-body 1.0.1", + "http-body 1.1.0", "httparse", "itoa", "pin-project-lite", @@ -2540,9 +2510,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ca68d021ef39cf6463ab54c1d0f5daf03377b70561305bb89a8f83aab66e0f" dependencies = [ "http 1.4.2", - "hyper 1.10.1", + "hyper 1.11.0", "hyper-util", - "rustls 0.23.41", + "rustls 0.23.42", "tokio", "tokio-rustls 0.26.4", "tower-service", @@ -2559,13 +2529,13 @@ dependencies = [ "futures-channel", "futures-util", "http 1.4.2", - "http-body 1.0.1", - "hyper 1.10.1", + "http-body 1.1.0", + "hyper 1.11.0", "ipnet", "libc", "percent-encoding", "pin-project-lite", - "socket2 0.6.4", + "socket2 0.6.5", "system-configuration", "tokio", "tower-service", @@ -2735,23 +2705,13 @@ version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8fae54786f62fb2918dcfae3d568594e50eb9b5c25bf04371af6fe7516452fb" -[[package]] -name = "inout" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" -dependencies = [ - "block-padding", - "generic-array", -] - [[package]] name = "ipconfig" version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d40460c0ce33d6ce4b0630ad68ff63d6661961c48b6dba35e5a4d81cfb48222" dependencies = [ - "socket2 0.6.4", + "socket2 0.6.5", "widestring", "windows-registry", "windows-result", @@ -2801,11 +2761,12 @@ checksum = "47f142fe24a9c9944451e8349de0a56af5f3e7226dc46f3ed4d4ecc0b85af75e" [[package]] name = "jiff" -version = "0.2.31" +version = "0.2.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccfe6121cbe750cf81efa362d85c0bde7ea298ec43092d3a193baca59cdbd634" +checksum = "e184d09547b80eb7e20d141ba2fb1fbac843ca53f4cf1b31210adc4c1adc6e16" dependencies = [ "defmt", + "jiff-core", "jiff-static", "jiff-tzdb-platform", "js-sys", @@ -2817,22 +2778,32 @@ dependencies = [ "windows-link", ] +[[package]] +name = "jiff-core" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7feca88439efe53da3754500c1851dedf3cb36c524dd5cf8225cc0794de95d09" +dependencies = [ + "defmt", +] + [[package]] name = "jiff-static" -version = "0.2.31" +version = "0.2.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e165e897f662d428f3cd3828a919dbe067c2d42bb1031eede74ef9d27ecdedd2" +checksum = "323da076b7a6faf914dc677cb05a4b907742ff7375c8322c9e7f5061e5e0e9de" dependencies = [ + "jiff-core", "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] name = "jiff-tzdb" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6142247df1a93c2b3587402a19710be3e6e942f1581a1702e76408f2c21d6590" +checksum = "142bd39932ad231f10513df9ab62661fead8719872150b7ad02a2df79f4e141e" [[package]] name = "jiff-tzdb-platform" @@ -2855,7 +2826,7 @@ dependencies = [ "jni-sys", "log", "simd_cesu8", - "thiserror 2.0.18", + "thiserror 2.0.19", "walkdir", "windows-link", ] @@ -2870,7 +2841,7 @@ dependencies = [ "quote", "rustc_version", "simd_cesu8", - "syn", + "syn 2.0.119", ] [[package]] @@ -2889,7 +2860,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" dependencies = [ "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -2926,9 +2897,9 @@ dependencies = [ [[package]] name = "jsonwebtoken" -version = "10.4.0" +version = "11.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eba32bfb4ffdeaca3e34431072faf01745c9b26d25504aa7a6cf5684334fc4fc" +checksum = "881733cbc631fc9e472e24447ce32a64bedf2da498d6d8570b08edc87de71f65" dependencies = [ "base64 0.22.1", "ed25519-dalek", @@ -2938,7 +2909,7 @@ dependencies = [ "p256", "p384", "pem", - "rand 0.8.6", + "rand 0.8.7", "rsa", "serde", "serde_json", @@ -2996,10 +2967,10 @@ dependencies = [ "nom 8.0.0", "percent-encoding", "quoted_printable", - "rustls 0.23.41", + "rustls 0.23.42", "rustls-native-certs", "serde", - "socket2 0.6.4", + "socket2 0.6.5", "tokio", "tokio-rustls 0.26.4", "tracing", @@ -3008,9 +2979,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.186" +version = "0.2.189" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" +checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2" [[package]] name = "libm" @@ -3094,7 +3065,7 @@ name = "macros" version = "0.1.0" dependencies = [ "quote", - "syn", + "syn 3.0.3", ] [[package]] @@ -3185,9 +3156,9 @@ dependencies = [ [[package]] name = "mio" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02bd0af71c67b473010cbbc60715ee815645a4dc942899111f494b4b737d6fda" +checksum = "30d65c71f1ce40ab09135ce117d742b9f8a19ff91a41a8b57ed50bc2de59c427" dependencies = [ "libc", "wasi", @@ -3305,7 +3276,7 @@ dependencies = [ "num-integer", "num-iter", "num-traits", - "rand 0.8.6", + "rand 0.8.7", "smallvec", "zeroize", ] @@ -3318,13 +3289,13 @@ checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" [[package]] name = "num-derive" -version = "0.4.2" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +checksum = "e4e98dc3b890f6c23a0f9d3d491a2823d0dea0fa656302a13dd225fa924112a8" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 3.0.3", ] [[package]] @@ -3400,7 +3371,7 @@ dependencies = [ "chrono", "getrandom 0.2.17", "http 1.4.2", - "rand 0.8.6", + "rand 0.8.7", "serde", "serde_json", "serde_path_to_error", @@ -3450,7 +3421,7 @@ dependencies = [ "bytes", "futures", "http 1.4.2", - "http-body 1.0.1", + "http-body 1.1.0", "jiff", "log", "md-5", @@ -3519,7 +3490,7 @@ dependencies = [ "oauth2", "p256", "p384", - "rand 0.8.6", + "rand 0.8.7", "rsa", "serde", "serde-value", @@ -3539,7 +3510,7 @@ version = "0.10.81" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77823a27f0babb03091cb9ed9ef80af3b39dbc82f97e8fa530374b7dafd87a45" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "cfg-if", "foreign-types", "libc", @@ -3555,7 +3526,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -3687,16 +3658,6 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2ee67f1008b1ba2321834326597b8e186293b049a023cdef258527550b9935b4" -[[package]] -name = "pbkdf2" -version = "0.12.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" -dependencies = [ - "digest 0.10.7", - "hmac 0.12.1", -] - [[package]] name = "pear" version = "0.2.9" @@ -3717,7 +3678,7 @@ dependencies = [ "proc-macro2", "proc-macro2-diagnostics", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -3747,9 +3708,9 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "pest" -version = "2.8.7" +version = "2.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47627dd7305c6a2d6c8c6bcd24c5a4c17dbbf425f4f9c5313e724b38fc9782e9" +checksum = "7df728be843c7070fab6ab7c328c4e9e9d78e23bf749c0669c86ee7ebfa050a2" dependencies = [ "memchr", "ucd-trie", @@ -3757,9 +3718,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.8.7" +version = "2.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b4254325ecad416ab689e27ba51da03ba01a9632bc6e108f5fe7c3c4ad29d58" +checksum = "9e2dd6fc3b26b3462ee188aac870f5a41d398f1cd5e2408d16531bd71c9591fd" dependencies = [ "pest", "pest_generator", @@ -3767,22 +3728,22 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.8.7" +version = "2.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c4c0e91ead7a8f7acecbca6f003fc2e8282b1dbe2dd9c9d2f16aba42995e0a7" +checksum = "6a7a9205cfb6f596a9e8b689c0a15f9ceb7a1aafae7aaf788150ac65b29975b6" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] name = "pest_meta" -version = "2.8.7" +version = "2.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9744bc48116fee06334924bb5f2bad41eed5e89bd26e29b0b799f9a3f82c210" +checksum = "85abd351c0de1e8384fc791a0737111a350394937e92b956b743dac12429f57c" dependencies = [ "pest", ] @@ -3813,7 +3774,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" dependencies = [ "phf_shared 0.11.3", - "rand 0.8.6", + "rand 0.8.7", ] [[package]] @@ -3826,7 +3787,7 @@ dependencies = [ "phf_shared 0.11.3", "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -3887,21 +3848,6 @@ dependencies = [ "spki", ] -[[package]] -name = "pkcs5" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e847e2c91a18bfa887dd028ec33f2fe6f25db77db3619024764914affe8b69a6" -dependencies = [ - "aes", - "cbc", - "der", - "pbkdf2", - "scrypt", - "sha2 0.10.9", - "spki", -] - [[package]] name = "pkcs8" version = "0.10.2" @@ -3909,8 +3855,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" dependencies = [ "der", - "pkcs5", - "rand_core 0.6.4", "spki", ] @@ -3936,9 +3880,9 @@ dependencies = [ [[package]] name = "portable-atomic" -version = "1.13.1" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" +checksum = "3d20d5497ef88037a52ff98267d066e7f11fcc5e99bbfbd58a42336193aacec3" [[package]] name = "portable-atomic-util" @@ -4006,9 +3950,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.106" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" dependencies = [ "unicode-ident", ] @@ -4021,7 +3965,7 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", "version_check", "yansi", ] @@ -4075,9 +4019,9 @@ dependencies = [ [[package]] name = "quick-xml" -version = "0.40.1" +version = "0.41.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2474bd2e5029e7ccb6abb2ba48cf2383a333851dedf495901544281590c7da7f" +checksum = "e660451e55124f798a69a5af3f49ccfbefbd41910eefd25caf2393e1f3473ec1" dependencies = [ "memchr", "serde", @@ -4085,9 +4029,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.46" +version = "1.0.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368" +checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" dependencies = [ "proc-macro2", ] @@ -4123,9 +4067,9 @@ dependencies = [ [[package]] name = "rand" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" +checksum = "22f6172bdec972074665ed81ed53b71da00bfc44b65a753cfde883ec4c702a1a" dependencies = [ "libc", "rand_chacha 0.3.1", @@ -4134,9 +4078,9 @@ dependencies = [ [[package]] name = "rand" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" +checksum = "b9ef1d0d795eb7d84685bca4f72f3649f064e6641543d3a8c415898726a57b41" dependencies = [ "rand_chacha 0.9.0", "rand_core 0.9.5", @@ -4203,7 +4147,7 @@ version = "11.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "498cd0dc59d73224351ee52a95fee0f1a617a2eae0e7d9d720cc622c73a54186" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", ] [[package]] @@ -4212,34 +4156,34 @@ version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", ] [[package]] name = "ref-cast" -version = "1.0.25" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" +checksum = "216e8f773d7923bcba9ceb86a86c93cabb3903a11872fc3f138c49630e50b96d" dependencies = [ "ref-cast-impl", ] [[package]] name = "ref-cast-impl" -version = "1.0.25" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" +checksum = "2c9283685feec7d69af75fb0e858d5e7378f33fe4fc699383b2916ab9273e03c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 3.0.3", ] [[package]] name = "regex" -version = "1.12.4" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1292b7759ae1cb9ec195452d1390a074f0cd8541ab7a5a8c31cd6db45d4a6ba" +checksum = "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d" dependencies = [ "aho-corasick", "memchr", @@ -4249,9 +4193,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.14" +version = "0.4.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +checksum = "8fcfdb36bda0c880c5931cdc7a2bcdc8ba4556847b9d912bca70bc94708711ad" dependencies = [ "aho-corasick", "memchr", @@ -4283,9 +4227,9 @@ dependencies = [ [[package]] name = "reqsign-aws-v4" -version = "3.0.1" +version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b75624bd8a466e37ddc0a7b6c33ac859a85347c153a916e1dd9d0b68338f74a" +checksum = "4e9e1168fab3883ec6afed1c2e20c25b2a09f366cdb662ac3e0878ae0332d63e" dependencies = [ "anyhow", "bytes", @@ -4294,7 +4238,7 @@ dependencies = [ "http 1.4.2", "log", "percent-encoding", - "quick-xml 0.40.1", + "quick-xml 0.41.0", "reqsign-core", "rust-ini", "serde", @@ -4305,9 +4249,9 @@ dependencies = [ [[package]] name = "reqsign-core" -version = "3.0.1" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5fa5cb48808693614d1701fcd3db0b30fa292e0f18e122ae068b6d32eaeed3f" +checksum = "514a1e0b4aa288652a3fdbda4f0a610f379cdf5374e55a37c9edd03d57ed856b" dependencies = [ "anyhow", "base64 0.22.1", @@ -4320,9 +4264,6 @@ dependencies = [ "jiff", "log", "percent-encoding", - "rsa", - "serde", - "serde_json", "sha1 0.11.0", "sha2 0.11.0", "windows-sys 0.61.2", @@ -4330,9 +4271,9 @@ dependencies = [ [[package]] name = "reqsign-file-read-tokio" -version = "3.0.1" +version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a4b6f3a3fd29ffcc99a90aec585a65217783badfd73acddf847b63ae683bda9" +checksum = "2b472a8d1f2e5a4be8ce13bb7bdf4b59e9bee613ce124aca23959ddb42176b39" dependencies = [ "anyhow", "reqsign-core", @@ -4355,9 +4296,9 @@ dependencies = [ "futures-util", "h2", "http 1.4.2", - "http-body 1.0.1", + "http-body 1.1.0", "http-body-util", - "hyper 1.10.1", + "hyper 1.11.0", "hyper-rustls", "hyper-util", "js-sys", @@ -4365,7 +4306,7 @@ dependencies = [ "mime", "percent-encoding", "pin-project-lite", - "rustls 0.23.41", + "rustls 0.23.42", "rustls-pki-types", "rustls-platform-verifier", "serde", @@ -4454,7 +4395,7 @@ dependencies = [ "num_cpus", "parking_lot", "pin-project-lite", - "rand 0.8.6", + "rand 0.8.7", "ref-cast", "rocket_codegen", "rocket_http", @@ -4483,7 +4424,7 @@ dependencies = [ "proc-macro2", "quote", "rocket_http", - "syn", + "syn 2.0.119", "unicode-xid", "version_check", ] @@ -4553,7 +4494,6 @@ dependencies = [ "pkcs1", "pkcs8", "rand_core 0.6.4", - "sha2 0.10.9", "signature", "spki", "subtle", @@ -4567,7 +4507,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c51c9ae4df8a7fba42103df5c621fa3c37eccf3a3c650879e90fc48b11cc192c" dependencies = [ "hashbrown 0.16.1", - "thiserror 2.0.18", + "thiserror 2.0.19", ] [[package]] @@ -4614,7 +4554,7 @@ version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "errno", "libc", "linux-raw-sys", @@ -4635,9 +4575,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.41" +version = "0.23.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b92b125634d9b795e7beca796cc790df15a7fb38323bf3196fda83292d06b1f" +checksum = "3c54fcab019b409d04215d3a17cb438fd7fbf192ee61461f20f4fe18704bc138" dependencies = [ "log", "once_cell", @@ -4671,9 +4611,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.15.0" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "764899a24af3980067ee14bc143654f297b22eaebfe3c7b6b211920a5a59b046" +checksum = "2f4925028c7eb5d1fcdaf196971378ed9d2c1c4efc7dc5d011256f76c99c0a96" dependencies = [ "zeroize", ] @@ -4689,7 +4629,7 @@ dependencies = [ "jni", "log", "once_cell", - "rustls 0.23.41", + "rustls 0.23.42", "rustls-native-certs", "rustls-platform-verifier-android", "rustls-webpki 0.103.13", @@ -4738,15 +4678,6 @@ version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" -[[package]] -name = "salsa20" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213" -dependencies = [ - "cipher", -] - [[package]] name = "same-file" version = "1.0.6" @@ -4810,17 +4741,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" -[[package]] -name = "scrypt" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0516a385866c09368f0b5bcd1caff3366aace790fcd46e2bb032697bb172fd1f" -dependencies = [ - "pbkdf2", - "salsa20", - "sha2 0.10.9", -] - [[package]] name = "sct" version = "0.7.1" @@ -4851,7 +4771,7 @@ version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "core-foundation 0.10.1", "core-foundation-sys", "libc", @@ -4876,9 +4796,9 @@ checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" [[package]] name = "serde" -version = "1.0.228" +version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba" dependencies = [ "serde_core", "serde_derive", @@ -4906,29 +4826,29 @@ dependencies = [ [[package]] name = "serde_core" -version = "1.0.228" +version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.228" +version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 3.0.3", ] [[package]] name = "serde_json" -version = "1.0.150" +version = "1.0.151" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" +checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14" dependencies = [ "indexmap 2.14.0", "itoa", @@ -5017,14 +4937,14 @@ dependencies = [ "darling 0.23.0", "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] name = "sha1" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +checksum = "a978451301f4db1d02937a4ab3ccce137717b81826e79b7d49ffe3244a13c3b8" dependencies = [ "cfg-if", "cpufeatures 0.2.17", @@ -5111,15 +5031,15 @@ dependencies = [ [[package]] name = "simd-adler32" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" +checksum = "3a219298ac11a56ea9a6d2120044824d6f01aeb034955e7af7bc16858527deea" [[package]] name = "simd_cesu8" -version = "1.1.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94f90157bb87cddf702797c5dadfa0be7d266cdf49e22da2fcaa32eff75b2c33" +checksum = "11031e251abf8611c80f460e19dbdeb54a66db918e49c65a7065b46ac7aec520" dependencies = [ "rustc_version", "simdutf8", @@ -5139,7 +5059,7 @@ checksum = "0d585997b0ac10be3c5ee635f1bab02d512760d14b7c468801ac8a01d9ae5f1d" dependencies = [ "num-bigint", "num-traits", - "thiserror 2.0.18", + "thiserror 2.0.19", "time", ] @@ -5173,9 +5093,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.6.4" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51" +checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4" dependencies = [ "libc", "windows-sys 0.61.2", @@ -5183,9 +5103,9 @@ dependencies = [ [[package]] name = "spin" -version = "0.9.8" +version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +checksum = "3763264f6b73151db08c50ff20d7d8a0b8796e021cdea7ceedad07b80155fa0e" [[package]] name = "spinning_top" @@ -5269,9 +5189,20 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.118" +version = "2.0.119" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422" +checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3" dependencies = [ "proc-macro2", "quote", @@ -5295,7 +5226,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -5316,7 +5247,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "core-foundation 0.9.4", "system-configuration-sys", ] @@ -5361,11 +5292,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.18" +version = "2.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +checksum = "09a43598840e33d5b0331f38c5e30d13bb11c11210a4b58f0d9b18a5a5eefcd9" dependencies = [ - "thiserror-impl 2.0.18", + "thiserror-impl 2.0.19", ] [[package]] @@ -5376,25 +5307,25 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] name = "thiserror-impl" -version = "2.0.18" +version = "2.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +checksum = "43cbfe0cf76104d42a574802844187e84a305e531ed54455f11fbde0f10541cd" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 3.0.3", ] [[package]] name = "thread_local" -version = "1.1.9" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" +checksum = "1ad99c4c6d32803332c548b1af0540b357b3f5fc0be8f6c6bfe8b2e6ae784070" dependencies = [ "cfg-if", ] @@ -5410,9 +5341,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.53" +version = "0.3.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18dfaaeddcb932337b5e7866ee7d0ce9b76d2fd092997146f187ec09b4558a50" +checksum = "3e1d5e639ff6bab73cb6885cc7e7b1de96c3f32c68ec55f3952614bec1092244" dependencies = [ "deranged", "libc", @@ -5432,9 +5363,9 @@ checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" [[package]] name = "time-macros" -version = "0.2.31" +version = "0.2.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c431b87111666e491a90baa837f914fb45cd5dc3c268591b0220ff5057f2085f" +checksum = "7e689342a48d2ea927c87ea50cabf8594854bf940e9310208848d680d668ed85" dependencies = [ "num-conv", "time-core", @@ -5461,9 +5392,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" +checksum = "bb4ebadaa0af04fab11ae01eb5f9fdb5f9c5b875506e210e71c07873528baa7f" dependencies = [ "tinyvec_macros", ] @@ -5476,9 +5407,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.52.3" +version = "1.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe" +checksum = "202caea871b69668250d242070849eb495be178ed697a3e98aebce5bc81a0bed" dependencies = [ "bytes", "libc", @@ -5486,20 +5417,20 @@ dependencies = [ "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2 0.6.4", + "socket2 0.6.5", "tokio-macros", "windows-sys 0.61.2", ] [[package]] name = "tokio-macros" -version = "2.7.0" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" +checksum = "6328af13490e73a9b4694030fafd93f8c8c6a9dede33e821c3fc63eddf8042ba" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -5518,15 +5449,15 @@ version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" dependencies = [ - "rustls 0.23.41", + "rustls 0.23.42", "tokio", ] [[package]] name = "tokio-stream" -version = "0.1.18" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32da49809aab5c3bc678af03902d4ccddea2a87d028d86392a4b1560c6906c70" +checksum = "a3d06f0b082ba57c26b79407372e57cf2a1e28124f78e9479fe80322cf53420b" dependencies = [ "futures-core", "pin-project-lite", @@ -5547,14 +5478,15 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.18" +version = "0.7.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" +checksum = "494815d09bf52b5548659851081238f0ca39ff638363907596da739561c62c52" dependencies = [ "bytes", "futures-core", "futures-io", "futures-sink", + "libc", "pin-project-lite", "tokio", ] @@ -5622,7 +5554,7 @@ version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" dependencies = [ - "winnow 1.0.3", + "winnow 1.0.4", ] [[package]] @@ -5639,7 +5571,7 @@ checksum = "f8e43134db17199f7f721803383ac5854edd0d3d523cc34dba321d6acfbe76c3" dependencies = [ "digest 0.10.7", "hmac 0.12.1", - "sha1 0.10.6", + "sha1 0.10.7", "sha2 0.10.9", ] @@ -5665,12 +5597,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840" dependencies = [ "async-compression", - "bitflags 2.13.0", + "bitflags 2.13.1", "bytes", "futures-core", "futures-util", "http 1.4.2", - "http-body 1.0.1", + "http-body 1.1.0", "http-body-util", "pin-project-lite", "tokio", @@ -5713,7 +5645,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -5773,8 +5705,8 @@ dependencies = [ "http 1.4.2", "httparse", "log", - "rand 0.8.6", - "sha1 0.10.6", + "rand 0.8.7", + "sha1 0.10.7", "thiserror 1.0.69", "url", "utf-8", @@ -5868,9 +5800,9 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "uuid" -version = "1.23.4" +version = "1.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf80a72845275afea99e7f2b434723d3bc7e38470fcd1c7ed39a599c73319a53" +checksum = "bf3923a6f5c4c6382e0b653c4117f48d631ea17f38ed86e2a828e6f7412f5239" dependencies = [ "getrandom 0.4.3", "js-sys", @@ -5886,9 +5818,9 @@ checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" [[package]] name = "value-bag" -version = "1.12.0" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ba6f5989077681266825251a52748b8c1d8a4ad098cc37e440103d0ea717fc0" +checksum = "ef73bfbaf3216cb59c205d7176bee1194e0d84348979da31f4a71fefe3c2054e" [[package]] name = "vaultwarden" @@ -5922,6 +5854,7 @@ dependencies = [ "hickory-resolver", "html5gum", "http 1.4.2", + "ipnet", "job_scheduler_ng", "jsonwebtoken", "lettre", @@ -5948,7 +5881,7 @@ dependencies = [ "rocket", "rocket_ws", "rpassword", - "rustls 0.23.41", + "rustls 0.23.42", "semver", "serde", "serde_json", @@ -6063,7 +5996,7 @@ dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn", + "syn 2.0.119", "wasm-bindgen-shared", ] @@ -6150,7 +6083,7 @@ dependencies = [ "nom 7.1.3", "openssl", "openssl-sys", - "rand 0.9.4", + "rand 0.9.5", "rand_chacha 0.9.0", "serde", "serde_cbor_2", @@ -6179,18 +6112,18 @@ dependencies = [ [[package]] name = "webpki-root-certs" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d46a5a140e6f7afeccd8eae97eff335163939eac8b929834875168b29b3d267" +checksum = "b96554aa2acc8ccdb7e1c9a58a7a68dd5d13bccc69cd124cb09406db612a1c9b" dependencies = [ "rustls-pki-types", ] [[package]] name = "which" -version = "8.0.4" +version = "8.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48d7cd18d4acb58fb3cdfe9ea54e6cd96a4e7d4cc45c56338b236e82dad47248" +checksum = "8f3ef584124b911bcc3875c2f1472e80f24361ceb789bd1c62b3e9a3df9ff43c" dependencies = [ "libc", ] @@ -6262,7 +6195,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -6273,7 +6206,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -6479,9 +6412,9 @@ dependencies = [ [[package]] name = "winnow" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" +checksum = "23b97319f7b8343df12cc98938e5c3eb436064524c8d2b4e30a1d3a36eecdf81" [[package]] name = "wit-bindgen" @@ -6562,7 +6495,7 @@ checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", "synstructure", ] @@ -6576,30 +6509,30 @@ dependencies = [ "form_urlencoded", "futures", "hmac 0.12.1", - "rand 0.9.4", + "rand 0.9.5", "reqwest", - "sha1 0.10.6", + "sha1 0.10.7", "threadpool", ] [[package]] name = "zerocopy" -version = "0.8.53" +version = "0.8.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75726053136156d419e285b9b7eddaaea9e3fea6ce32eed44a89901f0bd98de1" +checksum = "b5a105cd7b140f6eeec8acff2ea38135d3cab283ada58540f629fe51e46696eb" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.53" +version = "0.8.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4714fd92cf900833d49538023a9b3915155210801d1c1169eba513b2addefd71" +checksum = "0fe976fb70c78cd64cccfe3a6fc142244e8a77b70959b30faf9d0ac37ee228eb" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -6619,7 +6552,7 @@ checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", "synstructure", ] @@ -6640,7 +6573,7 @@ checksum = "3c50655cbb0fe3fc43170059e702f1ce5e19b84cec58dc87b037a09935c2f328" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -6673,14 +6606,14 @@ checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] name = "zmij" -version = "1.0.21" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" +checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" [[package]] name = "zstd" diff --git a/Cargo.toml b/Cargo.toml index 909570e7..db685864 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [workspace.package] edition = "2024" -rust-version = "1.94.1" +rust-version = "1.95.0" license = "AGPL-3.0-only" repository = "https://github.com/dani-garcia/vaultwarden" publish = false @@ -75,7 +75,7 @@ dotenvy = { version = "0.15.7", default-features = false } # Numerical libraries num-traits = "0.2.19" -num-derive = "0.4.2" +num-derive = "0.5.1" bigdecimal = "0.4.10" # Web framework @@ -89,8 +89,8 @@ rmpv = "1.3.1" # MessagePack library dashmap = "6.2.1" # Async futures -futures = "0.3.32" -tokio = { version = "1.52.3", features = [ +futures = "0.3.33" +tokio = { version = "1.53.1", features = [ "fs", "io-util", "net", @@ -99,14 +99,14 @@ tokio = { version = "1.52.3", features = [ "signal", "time", ] } -tokio-util = { version = "0.7.18", features = ["compat"] } +tokio-util = { version = "0.7.19", features = ["compat"] } # A generic serialization/deserialization framework -serde = { version = "1.0.228", features = ["derive"] } -serde_json = "1.0.150" +serde = { version = "1.0.229", features = ["derive"] } +serde_json = "1.0.151" # A safe, extensible ORM and Query builder -diesel = { version = "2.3.10", features = ["chrono", "r2d2", "numeric"] } +diesel = { version = "2.3.11", features = ["chrono", "r2d2", "numeric"] } diesel_migrations = "2.3.2" derive_more = { version = "2.1.1", features = [ @@ -124,16 +124,16 @@ libsqlite3-sys = { version = "0.37.0", optional = true } # Crypto-related libraries rand = "0.10.2" ring = "0.17.14" -rustls = { version = "0.23.41", features = ["ring", "std"], default-features = false } +rustls = { version = "0.23.42", features = ["ring", "std"], default-features = false } subtle = "2.6.1" # UUID generation -uuid = { version = "1.23.4", features = ["v4"] } +uuid = { version = "1.24.0", features = ["v4"] } # Date and time libraries chrono = { version = "0.4.45", default-features = false, features = ["clock", "serde"] } chrono-tz = "0.10.4" -time = "0.3.53" +time = "0.3.54" # Job scheduler job_scheduler_ng = "2.4.0" @@ -142,7 +142,7 @@ job_scheduler_ng = "2.4.0" data-encoding = "2.11.0" # JWT library -jsonwebtoken = { version = "10.4.0", default-features = false, features = ["rust_crypto", "use_pem"] } +jsonwebtoken = { version = "11.0.0", default-features = false, features = ["rust_crypto", "use_pem"] } # TOTP library totp-lite = "2.0.1" @@ -179,7 +179,7 @@ percent-encoding = "2.3.2" # URL encoding library used for URL's in the emails email_address = "0.2.9" # HTML Template library -handlebars = { version = "6.4.2", features = ["dir_source"] } +handlebars = { version = "6.4.3", features = ["dir_source"] } # HTTP client (Used for favicons, version check, DUO and HIBP API) reqwest = { version = "0.13.4", default-features = false, features = [ @@ -204,7 +204,7 @@ hickory-resolver = "0.26.1" # Favicon extraction libraries html5gum = "0.8.4" -regex = { version = "1.12.4", default-features = false, features = [ +regex = { version = "1.13.1", default-features = false, features = [ "perf", "std", "unicode-perl", @@ -230,6 +230,9 @@ pico-args = "0.5.0" pastey = "0.2.3" governor = "0.10.4" +# CIDR parsing for the trusted proxies of the client IP header +ipnet = "2.12.0" + # OIDC for SSO openidconnect = { version = "4.0.1", default-features = false } moka = { version = "0.12.15", features = ["future"] } @@ -241,7 +244,7 @@ semver = "1.0.28" # Mainly used for the musl builds, since the default musl malloc is very slow mimalloc = { version = "0.1.52", optional = true, default-features = false, features = ["secure"] } -which = "8.0.4" +which = "8.0.5" # Argon2 library with support for the PHC format argon2 = "0.5.3" @@ -256,17 +259,17 @@ grass_compiler = { version = "0.13.4", default-features = false } opendal = { version = "0.57.0", default-features = false, features = ["services-fs"] } # For retrieving AWS credentials, including temporary SSO credentials -aws-config = { version = "1.8.18", optional = true, default-features = false, features = [ +aws-config = { version = "1.10.0", optional = true, default-features = false, features = [ "behavior-version-latest", "credentials-process", "rt-tokio", "sso", ] } -aws-credential-types = { version = "1.2.14", optional = true } -aws-smithy-runtime-api = { version = "1.13.0", optional = true } +aws-credential-types = { version = "1.3.0", optional = true } +aws-smithy-runtime-api = { version = "1.14.0", optional = true } http = { version = "1.4.2", optional = true } -reqsign-aws-v4 = { version = "3.0.1", optional = true } -reqsign-core = { version = "3.0.1", optional = true } +reqsign-aws-v4 = { version = "3.0.2", optional = true } +reqsign-core = { version = "3.1.0", optional = true } # Strip debuginfo from the release builds # The debug symbols are to provide better panic traces diff --git a/docker/DockerSettings.yaml b/docker/DockerSettings.yaml index 1d765305..4a51a6b2 100644 --- a/docker/DockerSettings.yaml +++ b/docker/DockerSettings.yaml @@ -1,11 +1,11 @@ --- -vault_version: "v2026.6.2" -vault_image_digest: "sha256:f004f72a5d357b87483839500a517da3d1b4ea0a57b9731989d298cccea7d02a" +vault_version: "v2026.6.4" +vault_image_digest: "sha256:e7d3b31ec6a991a6bf447721ea341b4192ce5d3b920929211672fd4f3f891427" # Cross Compile Docker Helper Scripts v1.9.0 # 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:c64defb9ed5a91eacb37f96ccc3d4cd72521c4bd18d5442905b95e2226b0e707" -rust_version: 1.96.1 # Rust version to be used +rust_version: 1.97.1 # Rust version to be used debian_version: trixie # Debian release name to be used alpine_version: "3.24" # 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 494411fb..baa4c979 100644 --- a/docker/Dockerfile.alpine +++ b/docker/Dockerfile.alpine @@ -19,23 +19,23 @@ # - From https://hub.docker.com/r/vaultwarden/web-vault/tags, # click the tag name to view the digest of the image it currently points to. # - From the command line: -# $ docker pull docker.io/vaultwarden/web-vault:v2026.6.2 -# $ docker image inspect --format "{{.RepoDigests}}" docker.io/vaultwarden/web-vault:v2026.6.2 -# [docker.io/vaultwarden/web-vault@sha256:f004f72a5d357b87483839500a517da3d1b4ea0a57b9731989d298cccea7d02a] +# $ docker pull docker.io/vaultwarden/web-vault:v2026.6.4 +# $ docker image inspect --format "{{.RepoDigests}}" docker.io/vaultwarden/web-vault:v2026.6.4 +# [docker.io/vaultwarden/web-vault@sha256:e7d3b31ec6a991a6bf447721ea341b4192ce5d3b920929211672fd4f3f891427] # # - Conversely, to get the tag name from the digest: -# $ docker image inspect --format "{{.RepoTags}}" docker.io/vaultwarden/web-vault@sha256:f004f72a5d357b87483839500a517da3d1b4ea0a57b9731989d298cccea7d02a -# [docker.io/vaultwarden/web-vault:v2026.6.2] +# $ docker image inspect --format "{{.RepoTags}}" docker.io/vaultwarden/web-vault@sha256:e7d3b31ec6a991a6bf447721ea341b4192ce5d3b920929211672fd4f3f891427 +# [docker.io/vaultwarden/web-vault:v2026.6.4] # -FROM --platform=linux/amd64 docker.io/vaultwarden/web-vault@sha256:f004f72a5d357b87483839500a517da3d1b4ea0a57b9731989d298cccea7d02a AS vault +FROM --platform=linux/amd64 docker.io/vaultwarden/web-vault@sha256:e7d3b31ec6a991a6bf447721ea341b4192ce5d3b920929211672fd4f3f891427 AS vault ########################## ALPINE BUILD IMAGES ########################## ## NOTE: The Alpine Base Images do not support other platforms then linux/amd64 and linux/arm64 ## And for Alpine we define all build images here, they will only be loaded when actually used -FROM --platform=$BUILDPLATFORM ghcr.io/blackdex/rust-musl:x86_64-musl-stable-1.96.1 AS build_amd64 -FROM --platform=$BUILDPLATFORM ghcr.io/blackdex/rust-musl:aarch64-musl-stable-1.96.1 AS build_arm64 -FROM --platform=$BUILDPLATFORM ghcr.io/blackdex/rust-musl:armv7-musleabihf-stable-1.96.1 AS build_armv7 -FROM --platform=$BUILDPLATFORM ghcr.io/blackdex/rust-musl:arm-musleabi-stable-1.96.1 AS build_armv6 +FROM --platform=$BUILDPLATFORM ghcr.io/blackdex/rust-musl:x86_64-musl-stable-1.97.1 AS build_amd64 +FROM --platform=$BUILDPLATFORM ghcr.io/blackdex/rust-musl:aarch64-musl-stable-1.97.1 AS build_arm64 +FROM --platform=$BUILDPLATFORM ghcr.io/blackdex/rust-musl:armv7-musleabihf-stable-1.97.1 AS build_armv7 +FROM --platform=$BUILDPLATFORM ghcr.io/blackdex/rust-musl:arm-musleabi-stable-1.97.1 AS build_armv6 ########################## BUILD IMAGE ########################## # hadolint ignore=DL3006 diff --git a/docker/Dockerfile.debian b/docker/Dockerfile.debian index 5de9fdb5..7ebb07bd 100644 --- a/docker/Dockerfile.debian +++ b/docker/Dockerfile.debian @@ -19,15 +19,15 @@ # - From https://hub.docker.com/r/vaultwarden/web-vault/tags, # click the tag name to view the digest of the image it currently points to. # - From the command line: -# $ docker pull docker.io/vaultwarden/web-vault:v2026.6.2 -# $ docker image inspect --format "{{.RepoDigests}}" docker.io/vaultwarden/web-vault:v2026.6.2 -# [docker.io/vaultwarden/web-vault@sha256:f004f72a5d357b87483839500a517da3d1b4ea0a57b9731989d298cccea7d02a] +# $ docker pull docker.io/vaultwarden/web-vault:v2026.6.4 +# $ docker image inspect --format "{{.RepoDigests}}" docker.io/vaultwarden/web-vault:v2026.6.4 +# [docker.io/vaultwarden/web-vault@sha256:e7d3b31ec6a991a6bf447721ea341b4192ce5d3b920929211672fd4f3f891427] # # - Conversely, to get the tag name from the digest: -# $ docker image inspect --format "{{.RepoTags}}" docker.io/vaultwarden/web-vault@sha256:f004f72a5d357b87483839500a517da3d1b4ea0a57b9731989d298cccea7d02a -# [docker.io/vaultwarden/web-vault:v2026.6.2] +# $ docker image inspect --format "{{.RepoTags}}" docker.io/vaultwarden/web-vault@sha256:e7d3b31ec6a991a6bf447721ea341b4192ce5d3b920929211672fd4f3f891427 +# [docker.io/vaultwarden/web-vault:v2026.6.4] # -FROM --platform=linux/amd64 docker.io/vaultwarden/web-vault@sha256:f004f72a5d357b87483839500a517da3d1b4ea0a57b9731989d298cccea7d02a AS vault +FROM --platform=linux/amd64 docker.io/vaultwarden/web-vault@sha256:e7d3b31ec6a991a6bf447721ea341b4192ce5d3b920929211672fd4f3f891427 AS vault ########################## Cross Compile Docker Helper Scripts ########################## ## We use the linux/amd64 no matter which Build Platform, since these are all bash scripts @@ -36,7 +36,7 @@ FROM --platform=linux/amd64 docker.io/tonistiigi/xx@sha256:c64defb9ed5a91eacb37f ########################## BUILD IMAGE ########################## # hadolint ignore=DL3006 -FROM --platform=$BUILDPLATFORM docker.io/library/rust:1.96.1-slim-trixie AS build +FROM --platform=$BUILDPLATFORM docker.io/library/rust:1.97.1-slim-trixie AS build COPY --from=xx / / ARG TARGETARCH ARG TARGETVARIANT diff --git a/macros/Cargo.toml b/macros/Cargo.toml index d36b3e46..f059a214 100644 --- a/macros/Cargo.toml +++ b/macros/Cargo.toml @@ -13,8 +13,8 @@ path = "src/lib.rs" proc-macro = true [dependencies] -quote = "1.0.46" -syn = "2.0.118" +quote = "1.0.47" +syn = "3.0.3" [lints] workspace = true diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 6c32b3e0..9c5862a2 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,4 +1,4 @@ [toolchain] -channel = "1.96.1" +channel = "1.97.1" components = [ "rustfmt", "clippy" ] profile = "minimal" diff --git a/src/api/core/accounts.rs b/src/api/core/accounts.rs index 623edf24..0cb4d3c0 100644 --- a/src/api/core/accounts.rs +++ b/src/api/core/accounts.rs @@ -15,7 +15,7 @@ use crate::{ core::{accept_org_invite, log_user_event, two_factor::email}, master_password_policy, register_push_device, unregister_push_device, }, - auth::{ClientHeaders, Headers, decode_delete, decode_invite, decode_verify_email}, + auth::{ClientHeaders, ClientIp, Headers, decode_delete, decode_invite, decode_verify_email}, crypto, db::{ DbConn, DbPool, @@ -693,10 +693,6 @@ struct UnlockData { #[derive(Deserialize)] #[serde(rename_all = "camelCase")] struct ChangeKdfData { - #[allow(dead_code)] - new_master_password_hash: String, - #[allow(dead_code)] - key: String, authentication_data: AuthenticationData, unlock_data: UnlockData, master_password_hash: String, @@ -1197,7 +1193,9 @@ struct DeleteRecoverData { } #[post("/accounts/delete-recover", data = "")] -async fn post_delete_recover(data: Json, conn: DbConn) -> EmptyResult { +async fn post_delete_recover(data: Json, ip: ClientIp, conn: DbConn) -> EmptyResult { + crate::ratelimit::check_limit_unauthenticated(&ip.ip)?; + let data: DeleteRecoverData = data.into_inner(); if CONFIG.mail_enabled() { @@ -1270,9 +1268,11 @@ struct PasswordHintData { } #[post("/accounts/password-hint", data = "")] -async fn password_hint(data: Json, conn: DbConn) -> EmptyResult { +async fn password_hint(data: Json, ip: ClientIp, conn: DbConn) -> EmptyResult { const NO_HINT: &str = "Sorry, you have no password hint..."; + crate::ratelimit::check_limit_unauthenticated(&ip.ip)?; + if !CONFIG.password_hints_allowed() || (!CONFIG.mail_enabled() && !CONFIG.show_password_hint()) { err!("This server is not configured to provide password hints."); } @@ -1334,6 +1334,13 @@ pub async fn prelogin(data: Json, conn: DbConn) -> Json { "kdfIterations": kdf_iter, "kdfMemory": kdf_mem, "kdfParallelism": kdf_para, + "kdfSettings": { + "iterations": kdf_iter, + "kdfType": kdf_type, + "memory": kdf_mem, + "parallelism": kdf_para + }, + "salt": null, })) } @@ -1510,7 +1517,9 @@ async fn put_device_token(device_id: DeviceId, data: Json, headers: H } #[put("/devices/identifier//clear-token")] -async fn put_clear_device_token(device_id: DeviceId, conn: DbConn) -> EmptyResult { +async fn put_clear_device_token(device_id: DeviceId, ip: ClientIp, conn: DbConn) -> EmptyResult { + crate::ratelimit::check_limit_unauthenticated(&ip.ip)?; + // This only clears push token // https://github.com/bitwarden/server/blob/9ebe16587175b1c0e9208f84397bb75d0d595510/src/Api/Controllers/DevicesController.cs#L215 // https://github.com/bitwarden/server/blob/9ebe16587175b1c0e9208f84397bb75d0d595510/src/Core/Services/Implementations/DeviceService.cs#L37 @@ -1532,8 +1541,8 @@ async fn put_clear_device_token(device_id: DeviceId, conn: DbConn) -> EmptyResul // On upstream server, both PUT and POST are declared. Implementing the POST method in case it would be useful somewhere #[post("/devices/identifier//clear-token")] -async fn post_clear_device_token(device_id: DeviceId, conn: DbConn) -> EmptyResult { - put_clear_device_token(device_id, conn).await +async fn post_clear_device_token(device_id: DeviceId, ip: ClientIp, conn: DbConn) -> EmptyResult { + put_clear_device_token(device_id, ip, conn).await } #[get("/tasks")] diff --git a/src/api/core/ciphers.rs b/src/api/core/ciphers.rs index 14e9f72f..0cdae612 100644 --- a/src/api/core/ciphers.rs +++ b/src/api/core/ciphers.rs @@ -450,7 +450,9 @@ pub async fn update_cipher_from_data( match Membership::find_confirmed_by_user_and_org(&headers.user.uuid, &org_id, conn).await { None => err!("You don't have permission to add item to organization"), Some(member) => { - if shared_to_collections.is_some() + // A non-empty list of collections implies the caller already validated the user's write + // access to them, so we can move the cipher into the organization on that basis. + if shared_to_collections.as_ref().is_some_and(|cols| !cols.is_empty()) || member.has_full_access() || cipher.is_write_accessible_to_user(&headers.user.uuid, conn).await { @@ -629,7 +631,7 @@ async fn post_ciphers_import(data: Json, headers: Headers, conn: DbC // Read and create the ciphers for (index, mut cipher_data) in data.ciphers.into_iter().enumerate() { - let folder_id = relations_map.get(&index).map(|i| folders[*i].clone()); + let folder_id = relations_map.get(&index).and_then(|i| folders.get(*i).cloned()); cipher_data.folder_id = folder_id; let mut cipher = Cipher::new(cipher_data.r#type, cipher_data.name.clone()); @@ -1043,6 +1045,13 @@ async fn share_cipher_by_uuid( err!("Cipher doesn't exist") }; + // `update_cipher_from_data()` rejects this too, but only after the collections below were + // already linked. There are no transactions, so that would leave the cipher linked to a + // collection of another organization. + if cipher.organization_uuid.is_some() && cipher.organization_uuid != data.cipher.organization_id { + err!("Organization mismatch. Please resync the client before updating the cipher") + } + let mut shared_to_collections = vec![]; if let Some(organization_id) = &data.cipher.organization_id { diff --git a/src/api/core/events.rs b/src/api/core/events.rs index 698a890f..5518fa3c 100644 --- a/src/api/core/events.rs +++ b/src/api/core/events.rs @@ -182,7 +182,10 @@ async fn post_events_collect(data: Json>, headers: Headers, .await; } 1600..=1699 => { - if let Some(org_id) = &event.organization_id { + // Only allow logging events for an organization the user is actually a member of. + if let Some(org_id) = &event.organization_id + && Membership::find_confirmed_by_user_and_org(&headers.user.uuid, org_id, &conn).await.is_some() + { log_event_impl( event.r#type, org_id, @@ -197,8 +200,11 @@ async fn post_events_collect(data: Json>, headers: Headers, } } _ => { + // The cipher determines the organization the event is logged to, so make sure the + // user can actually access it instead of trusting the provided cipher uuid. if let Some(cipher_uuid) = &event.cipher_id && let Some(cipher) = Cipher::find_by_uuid(cipher_uuid, &conn).await + && cipher.is_accessible_to_user(&headers.user.uuid, &conn).await && let Some(org_id) = cipher.organization_uuid { log_event_impl( diff --git a/src/api/core/mod.rs b/src/api/core/mod.rs index 2ea8ab21..a5ae50a4 100644 --- a/src/api/core/mod.rs +++ b/src/api/core/mod.rs @@ -228,14 +228,17 @@ fn config() -> Json { // Version history: // - Individual cipher key encryption: 2024.2.0 // - Mobile app support for MasterPasswordUnlockData: 2025.8.0 - "version": "2025.12.0", + "version": "2026.6.0", "gitHash": option_env!("GIT_REV"), "server": { "name": "Vaultwarden", "url": "https://github.com/dani-garcia/vaultwarden" }, "settings": { - "disableUserRegistration": CONFIG.is_signup_disabled() + "disableUserRegistration": CONFIG.is_signup_disabled(), + // When enabled, this setting signals to clients that onboarding interstitials + // (post-login welcome dialogs, extension install prompts, setup extension redirects, and premium upsell modals) should be suppressed + "suppressOnboardingInterstitials": false }, "environment": { "vault": domain, @@ -251,6 +254,10 @@ fn config() -> Json { "vapidPublicKey": null }, "featureStates": feature_states, + // Not supported right now + // Used for by clients to learn if the server requires extra work to establish a connection. + // See: https://github.com/bitwarden/server/pull/6892 | https://github.com/bitwarden/server/commit/52955d1860b4dfb905f67bbe39d9b10bbd61ded0 + "communication": null, "object": "config", })) } diff --git a/src/api/core/organizations.rs b/src/api/core/organizations.rs index c7e79aed..989ca47d 100644 --- a/src/api/core/organizations.rs +++ b/src/api/core/organizations.rs @@ -577,6 +577,13 @@ async fn post_bulk_access_collections( err!("Can't find organization details") } + // The collections and members are checked below, the groups only here. + let org_groups = Group::find_by_organization(&org_id, &conn).await; + let org_group_ids: HashSet<&GroupId> = org_groups.iter().map(|g| &g.uuid).collect(); + if let Some(g) = data.groups.iter().find(|g| !org_group_ids.contains(&g.id)) { + err!("Invalid group", format!("Group {} does not belong to organization {}!", g.id, org_id)) + } + for col_id in data.collection_ids { let Some(collection) = Collection::find_by_uuid_and_org(&col_id, &org_id, &conn).await else { err!("Collection not found") @@ -946,6 +953,11 @@ async fn get_members( if org_id != headers.membership.org_uuid { err!("Organization not found", "Organization id's do not match"); } + + if !headers.membership.has_full_access() { + err_code!("Resource not found.", "User does not have full access", rocket::http::Status::NotFound.code); + } + let mut users_json = Vec::new(); for u in Membership::find_by_org(&org_id, &conn).await { users_json.push( @@ -1167,6 +1179,9 @@ async fn send_invite( } for group_id in &data.groups { + if Group::find_by_uuid_and_org(group_id, &org_id, &conn).await.is_none() { + err!("Group not found in Organization") + } let mut group_entry = GroupUser::new(group_id.clone(), new_member.uuid.clone()); group_entry.save(&conn).await?; } @@ -1614,6 +1629,9 @@ async fn edit_member( GroupUser::delete_all_by_member(&member_to_edit.uuid, &conn).await?; for group_id in data.groups.iter().flatten() { + if Group::find_by_uuid_and_org(group_id, &org_id, &conn).await.is_none() { + err!("Group not found in Organization") + } let mut group_entry = GroupUser::new(group_id.clone(), member_to_edit.uuid.clone()); group_entry.save(&conn).await?; } @@ -1813,19 +1831,19 @@ 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 existing_collections: HashSet> = - Collection::find_by_organization(&org_id, &conn).await.into_iter().map(|c| Some(c.uuid)).collect(); + let existing_collections: HashMap = + Collection::find_by_organization(&org_id, &conn).await.into_iter().map(|c| (c.uuid.clone(), c)).collect(); let mut collections: Vec = Vec::with_capacity(data.collections.len()); for col in data.collections { - let collection_uuid = if existing_collections.contains(&col.id) { - let col_id = col.id.unwrap(); - // When not an Owner or Admin, check if the member is allowed to access the collection. + let existing = col.id.as_ref().and_then(|col_id| existing_collections.get(col_id)); + let collection_uuid = if let Some(collection) = existing { + // When not an Owner or Admin, check if the member is allowed to write to the collection. if headers.membership.atype < MembershipType::Admin - && !Collection::can_access_collection(&headers.membership, &col_id, &conn).await + && !collection.is_writable_by_user(&headers.membership.user_uuid, &conn).await { err!(Compact, "The current user isn't allowed to manage this collection") } - col_id + collection.uuid.clone() } else { // We do not allow users or managers which can not manage all collections to create new collections // If there is any collection other than an existing import collection, abort the import. @@ -1853,6 +1871,8 @@ async fn post_org_import( for mut cipher_data in data.ciphers { // Always clear folder_id's via an organization import cipher_data.folder_id = None; + // Replace the client-provided, unvalidated organizationId with the real target org + cipher_data.organization_id = Some(org_id.clone()); let mut cipher = Cipher::new(cipher_data.r#type, cipher_data.name.clone()); update_cipher_from_data( &mut cipher, @@ -1870,8 +1890,9 @@ async fn post_org_import( // Assign the collections for (cipher_index, col_index) in relations { - let cipher_id = &ciphers[cipher_index]; - let col_id = &collections[col_index]; + let (Some(cipher_id), Some(col_id)) = (ciphers.get(cipher_index), collections.get(col_index)) else { + err!(Compact, "Invalid collection relationship") + }; CollectionCipher::save(cipher_id, col_id, &conn).await?; } @@ -2441,6 +2462,23 @@ async fn get_groups_data( if org_id != headers.membership.org_uuid { err!("Organization not found", "Organization id's do not match"); } + + // The details view (group→collection/user mappings) needs full org access; the plain list only + // needs manage access to a collection, so a manager of a collection (directly or via a group) + // can load it to assign groups. + let has_full_access = headers.membership.has_full_access() + || (CONFIG.org_groups_enabled() + && GroupUser::has_full_access_by_member(&org_id, &headers.membership.uuid, &conn).await); + let allowed = if details { + has_full_access + } else { + has_full_access + || Collection::has_manageable_collection_by_user(&org_id, &headers.membership.user_uuid, &conn).await + }; + if !allowed { + err_code!("Resource not found.", "User does not have access", rocket::http::Status::NotFound.code); + } + let groups: Vec = if CONFIG.org_groups_enabled() { let groups = Group::find_by_organization(&org_id, &conn).await; let mut groups_json = Vec::with_capacity(groups.len()); diff --git a/src/api/core/public.rs b/src/api/core/public.rs index 33189e78..3db25df9 100644 --- a/src/api/core/public.rs +++ b/src/api/core/public.rs @@ -14,7 +14,7 @@ use crate::{ db::{ DbConn, models::{ - Group, GroupUser, Invitation, Membership, MembershipStatus, MembershipType, Organization, + Group, GroupUser, Invitation, Membership, MembershipStatus, MembershipType, OrgPolicy, Organization, OrganizationApiKey, OrganizationId, User, }, }, @@ -84,8 +84,15 @@ async fn ldap_import(data: Json, token: PublicToken, conn: DbConn } // If user is part of the organization, restore it } else if let Some(mut member) = Membership::find_by_email_and_org(&user_data.email, &org_id, &conn).await { - let restored = member.restore(); + let mut restored = member.restore(); let ext_modified = member.set_external_id(Some(user_data.external_id.clone())); + // Enforce org policies as every other restore path does. + // If the user is not allowed, we revoke again and continue so the external_id is still updated. + if restored && let Err(e) = OrgPolicy::check_user_allowed(&member, "restore", &conn).await { + warn!("Not restoring {}: {e:?}", user_data.email); + member.revoke(); + restored = false; + } if restored || ext_modified { member.save(&conn).await?; } diff --git a/src/api/core/sends.rs b/src/api/core/sends.rs index fb3ee48f..042ce95b 100644 --- a/src/api/core/sends.rs +++ b/src/api/core/sends.rs @@ -453,6 +453,9 @@ async fn post_access(headers: SendHeaders, conn: DbConn, nt: Notify<'_>) -> Json let Some(send) = Send::find_by_uuid(&headers.send_id, &conn).await else { err_code!(SEND_INACCESSIBLE_MSG, 404) }; + if !send.is_accessible() { + err_code!(SEND_INACCESSIBLE_MSG, 404) + } process_access(send, conn, nt).await } @@ -471,6 +474,8 @@ async fn post_access_legacy( ip: ClientIp, nt: Notify<'_>, ) -> JsonResult { + crate::ratelimit::check_limit_unauthenticated(&ip.ip)?; + let Some(mut send) = Send::find_by_access_id(access_id, &conn).await else { err_code!(SEND_INACCESSIBLE_MSG, 404) }; @@ -481,17 +486,7 @@ async fn post_access_legacy( err_code!(SEND_INACCESSIBLE_MSG, 404); } - if let Some(expiration) = send.expiration_date - && Utc::now().naive_utc() >= expiration - { - err_code!(SEND_INACCESSIBLE_MSG, 404) - } - - if Utc::now().naive_utc() >= send.deletion_date { - err_code!(SEND_INACCESSIBLE_MSG, 404) - } - - if send.disabled { + if !send.is_accessible() { err_code!(SEND_INACCESSIBLE_MSG, 404) } @@ -505,11 +500,13 @@ async fn post_access_legacy( // Files are incremented during the download if send.atype == SendType::Text as i32 { - send.access_count += 1; + if !send.register_access(&conn).await? { + err_code!(SEND_INACCESSIBLE_MSG, 404) + } + } else { + send.save(&conn).await?; } - send.save(&conn).await?; - process_access(send, conn, nt).await } @@ -537,6 +534,9 @@ async fn post_access_file( let Some(send) = Send::find_by_uuid(&headers.send_id, &conn).await else { err_code!(SEND_INACCESSIBLE_MSG, 404) }; + if !send.is_accessible() { + err_code!(SEND_INACCESSIBLE_MSG, 404) + } process_access_file(send, file_id, host, conn, nt).await } @@ -548,8 +548,11 @@ async fn post_access_file_legacy( data: Json, host: Host, conn: DbConn, + ip: ClientIp, nt: Notify<'_>, ) -> JsonResult { + crate::ratelimit::check_limit_unauthenticated(&ip.ip)?; + let Some(mut send) = Send::find_by_uuid(&send_id, &conn).await else { err_code!(SEND_INACCESSIBLE_MSG, 404) }; @@ -560,17 +563,7 @@ async fn post_access_file_legacy( err_code!(SEND_INACCESSIBLE_MSG, 404) } - if let Some(expiration) = send.expiration_date - && Utc::now().naive_utc() >= expiration - { - err_code!(SEND_INACCESSIBLE_MSG, 404) - } - - if Utc::now().naive_utc() >= send.deletion_date { - err_code!(SEND_INACCESSIBLE_MSG, 404) - } - - if send.disabled { + if !send.is_accessible() { err_code!(SEND_INACCESSIBLE_MSG, 404) } @@ -582,9 +575,9 @@ async fn post_access_file_legacy( } } - send.access_count += 1; - - send.save(&conn).await?; + if !send.register_access(&conn).await? { + err_code!(SEND_INACCESSIBLE_MSG, 404) + } process_access_file(send, file_id, host, conn, nt).await } diff --git a/src/api/icons.rs b/src/api/icons.rs index 81191e38..8f3e730b 100644 --- a/src/api/icons.rs +++ b/src/api/icons.rs @@ -405,6 +405,22 @@ async fn get_page(url: &str) -> Result { } async fn get_page_with_referer(url: &str, referer: &str) -> Result { + // The resolver only sees hosts needing name resolution, so IP-literal hrefs from + // attacker-controlled HTML never reach `post_resolve()`. Check them here. + let Ok(parsed_url) = url::Url::parse(url) else { + err_silent!("Invalid URL", url) + }; + + if !matches!(parsed_url.scheme(), "http" | "https") { + err_silent!("Invalid scheme", url) + } + + let Some(host) = parsed_url.host() else { + err_silent!("Invalid host", url) + }; + + should_block_host(&host)?; + let mut client = CLIENT.get(url); if !referer.is_empty() { client = client.header("Referer", referer); diff --git a/src/api/identity.rs b/src/api/identity.rs index 1597698f..9212ed8d 100644 --- a/src/api/identity.rs +++ b/src/api/identity.rs @@ -109,6 +109,7 @@ async fn login( } "authorization_code" => err!("SSO sign-in is not available"), "send_access" => { + crate::ratelimit::check_limit_unauthenticated(&client_header.ip.ip)?; check_is_some(data.client_id.as_ref(), "client_id cannot be blank")?; check_is_some(data.send_id.as_ref(), "send_id cannot be blank")?; @@ -1055,8 +1056,11 @@ enum RegisterVerificationResponse { #[post("/accounts/register/send-verification-email", data = "")] async fn register_verification_email( data: Json, + ip: ClientIp, conn: DbConn, ) -> ApiResult { + crate::ratelimit::check_limit_unauthenticated(&ip.ip)?; + let data = data.into_inner(); // the registration can only continue if signup is allowed or there exists an invitation diff --git a/src/api/notifications.rs b/src/api/notifications.rs index 80067433..8bfcd518 100644 --- a/src/api/notifications.rs +++ b/src/api/notifications.rs @@ -33,9 +33,14 @@ pub static WS_USERS: LazyLock> = LazyLock::new(|| { pub static WS_ANONYMOUS_SUBSCRIPTIONS: LazyLock> = LazyLock::new(|| { Arc::new(AnonymousWebSocketSubscriptions { map: Arc::new(dashmap::DashMap::new()), + connections: Arc::new(dashmap::DashMap::new()), }) }); +/// The anonymous hub needs no authentication, so bound how much a single client can hold open. +/// One connection is needed per pending login request, several at once are only expected behind NAT. +const MAX_ANONYMOUS_CONNECTIONS_PER_IP: u32 = 25; + static NOTIFICATIONS_DISABLED: LazyLock = LazyLock::new(|| !CONFIG.enable_websocket() && !CONFIG.push_enabled()); pub fn routes() -> Vec { @@ -82,14 +87,21 @@ impl Drop for WSEntryMapGuard { struct WSAnonymousEntryMapGuard { subscriptions: Arc, token: String, + entry_uuid: uuid::Uuid, addr: IpAddr, } impl WSAnonymousEntryMapGuard { - fn new(subscriptions: Arc, token: String, addr: IpAddr) -> Self { + fn new( + subscriptions: Arc, + token: String, + entry_uuid: uuid::Uuid, + addr: IpAddr, + ) -> Self { Self { subscriptions, token, + entry_uuid, addr, } } @@ -98,7 +110,11 @@ impl WSAnonymousEntryMapGuard { impl Drop for WSAnonymousEntryMapGuard { fn drop(&mut self) { info!("Closing WS connection from {}", self.addr); - self.subscriptions.map.remove(&self.token); + if let Some(mut entry) = self.subscriptions.map.get_mut(&self.token) { + entry.retain(|(uuid, _)| uuid != &self.entry_uuid); + } + self.subscriptions.map.remove_if(&self.token, |_, senders| senders.is_empty()); + self.subscriptions.release(self.addr); } } @@ -194,12 +210,19 @@ fn anonymous_websockets_hub<'r>(ws: WebSocket, token: String, ip: ClientIp) -> R let (mut rx, guard) = { let subscriptions = Arc::clone(&WS_ANONYMOUS_SUBSCRIPTIONS); - // Add a channel to send messages to this client to the map + if !subscriptions.try_reserve(ip.ip) { + err_code!("Too many connections", 429) + } + + // Add a channel to send messages to this client to the map. + // Clients reconnect with the same token while a login request is still pending, so keep + // every subscriber instead of replacing, otherwise the older one takes the newer one down. let (tx, rx) = tokio::sync::mpsc::channel::(100); - subscriptions.map.insert(token.clone(), tx); + let entry_uuid = uuid::Uuid::new_v4(); + subscriptions.map.entry(token.clone()).or_default().push((entry_uuid, tx)); // Once the guard goes out of scope, the connection will have been closed and the entry will be deleted from the map - (rx, WSAnonymousEntryMapGuard::new(subscriptions, token, ip.ip)) + (rx, WSAnonymousEntryMapGuard::new(subscriptions, token, entry_uuid, ip.ip)) }; Ok({ @@ -534,15 +557,42 @@ impl WebSocketUsers { #[derive(Clone)] pub struct AnonymousWebSocketSubscriptions { - map: Arc>>, + map: Arc>>, + connections: Arc>, } impl AnonymousWebSocketSubscriptions { + /// Takes a connection slot for this address, returns false when it already reached the limit. + fn try_reserve(&self, addr: IpAddr) -> bool { + let mut count = self.connections.entry(addr).or_insert(0); + if *count >= MAX_ANONYMOUS_CONNECTIONS_PER_IP { + return false; + } + *count += 1; + true + } + + /// Releases a slot taken by `try_reserve`. + fn release(&self, addr: IpAddr) { + let empty = if let Some(mut count) = self.connections.get_mut(&addr) { + *count = count.saturating_sub(1); + *count == 0 + } else { + false + }; + // Only remove once the guard above is dropped, otherwise this deadlocks. + if empty { + self.connections.remove_if(&addr, |_, count| *count == 0); + } + } + async fn send_update(&self, token: &str, data: &[u8]) { - if let Some(sender) = self.map.get(token).map(|v| v.clone()) - && let Err(e) = sender.send(Message::binary(data)).await - { - error!("Error sending WS update {e}"); + // Clone the senders so the map isn't kept locked while sending. + let senders = self.map.get(token).map(|v| v.clone()).unwrap_or_default(); + for (_, sender) in senders { + if let Err(e) = sender.send(Message::binary(data)).await { + error!("Error sending WS update {e}"); + } } } diff --git a/src/auth.rs b/src/auth.rs index 994e53dd..0aa5978d 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -10,6 +10,7 @@ use std::{ }; use chrono::{DateTime, TimeDelta, Utc}; +use ipnet::IpNet; use jsonwebtoken::{Algorithm, DecodingKey, EncodingKey, Header, errors::ErrorKind}; use num_traits::FromPrimitive; use openssl::rsa::Rsa; @@ -1075,12 +1076,44 @@ pub struct ClientIp { pub ip: IpAddr, } +/// Parses a single entry of `ip_header_trusted_proxies`, which can be a CIDR range or a plain IP. +pub fn parse_trusted_proxy(entry: &str) -> Option { + let entry = entry.trim(); + match entry.parse::() { + Ok(net) => Some(net), + // Without a prefix length it is a single address, which is a valid way to write this. + Err(_) => entry.parse::().ok().map(IpNet::from), + } +} + +/// The client IP header can be set by anyone able to reach us, so only accept it from a proxy we trust. +fn ip_header_is_trusted(remote: Option) -> bool { + let trusted = CONFIG.ip_header_trusted_proxies(); + let trusted = trusted.trim(); + if trusted.eq_ignore_ascii_case("all") { + return true; + } + + let Some(remote) = remote else { + return false; + }; + // A dual stack listener reports IPv4 clients as IPv4-mapped IPv6, which `is_global()` reports as + // non global. That is what we want when blocking outgoing requests, but here it would trust them. + let remote = remote.to_canonical(); + if trusted.eq_ignore_ascii_case("local") { + return !crate::util::is_global(remote); + } + trusted.split(',').filter_map(parse_trusted_proxy).any(|net| net.contains(&remote)) +} + #[rocket::async_trait] impl<'r> FromRequest<'r> for ClientIp { type Error = (); async fn from_request(req: &'r Request<'_>) -> Outcome { - let ip = if CONFIG._ip_header_enabled() { + let remote = req.remote().map(|r| r.ip()); + + let ip = if CONFIG._ip_header_enabled() && ip_header_is_trusted(remote) { req.headers().get_one(&CONFIG.ip_header()).and_then(|ip| { match ip.find(',') { Some(idx) => &ip[..idx], @@ -1091,10 +1124,15 @@ impl<'r> FromRequest<'r> for ClientIp { .ok() }) } else { + if CONFIG._ip_header_enabled() && req.headers().get_one(&CONFIG.ip_header()).is_some() { + // Log the canonical IP, which is what the user filter will need to match against + let remote = remote.map(|ip| ip.to_canonical()); + debug!("Ignoring the '{}' header, {remote:?} is not a trusted proxy", CONFIG.ip_header()); + } None }; - let ip = ip.or_else(|| req.remote().map(|r| r.ip())).unwrap_or_else(|| "0.0.0.0".parse().unwrap()); + let ip = ip.or(remote).unwrap_or_else(|| "0.0.0.0".parse().unwrap()); Outcome::Success(ClientIp { ip, @@ -1289,20 +1327,8 @@ pub async fn refresh_tokens( ) -> ApiResult<(Device, AuthTokens)> { let refresh_claims = match decode_refresh(refresh_token) { Err(err) => { - error!("Failed to decode {} refresh_token: {refresh_token}: {err:?}", ip.ip); - //err_silent!(format!("Impossible to read refresh_token: {}", err.message())) - - // If the token failed to decode, it was probably one of the old style tokens that was just a Base64 string. - // We can generate a claim for them for backwards compatibility. Note that the password refresh claims don't - // check expiration or issuer, so they're not included here. - RefreshJwtClaims { - nbf: 0, - exp: 0, - iss: String::new(), - sub: AuthMethod::Password, - device_token: refresh_token.into(), - token: None, - } + error!("Failed to decode refresh_token from {}: {err:?}", ip.ip); + err_silent!("Invalid refresh token") } Ok(claims) => claims, }; diff --git a/src/auth/send.rs b/src/auth/send.rs index 84500b6a..2554488a 100644 --- a/src/auth/send.rs +++ b/src/auth/send.rs @@ -85,18 +85,8 @@ impl SendTokens { return Self::invalid_error(&format!("Send {send_id}, max access reached"), "send_id_invalid", true); } - if let Some(expiration) = send.expiration_date - && Utc::now().naive_utc() >= expiration - { - return Self::invalid_error(&format!("Send {send_id}, expired"), "send_id_invalid", true); - } - - if Utc::now().naive_utc() >= send.deletion_date { - return Self::invalid_error(&format!("Send {send_id}, past deletion"), "send_id_invalid", true); - } - - if send.disabled { - return Self::invalid_error(&format!("Send {send_id}, disabled"), "send_id_invalid", true); + if !send.is_accessible() { + return Self::invalid_error(&format!("Send {send_id}, not accessible"), "send_id_invalid", true); } if send.password_hash.is_some() { @@ -113,8 +103,9 @@ impl SendTokens { } } - send.access_count += 1; - send.save(conn).await?; + if !send.register_access(conn).await? { + return Self::invalid_error(&format!("Send {send_id}, max access reached"), "send_id_invalid", true); + } Ok(Self { access_claims: generate_send_access_claims(&send_id), diff --git a/src/config.rs b/src/config.rs index 9ef31b61..8beec683 100644 --- a/src/config.rs +++ b/src/config.rs @@ -666,6 +666,12 @@ make_config! { ip_header: String, true, def, "X-Real-IP".to_owned(); /// Internal IP header property, used to avoid recomputing each time _ip_header_enabled: bool, false, generated, |c| &c.ip_header.trim().to_lowercase() != "none"; + /// Trusted proxies |> Which addresses the client IP header is accepted from. Requests from any + /// other address use the remote IP instead, so a client can't spoof the header. + /// Either the string "local" (the default, any non-global address, which covers a reverse proxy + /// running on the same host or container network), the string "all" to accept it from anywhere, + /// or a comma separated list of IPs and CIDR ranges. + ip_header_trusted_proxies: String, true, def, "local".to_owned(); /// Icon service |> The predefined icon services are: internal, bitwarden, duckduckgo, google. /// To specify a custom icon service, set a URL template with exactly one instance of `{}`, /// which is replaced with the domain. For example: `https://icon.example.com/domain/{}`. @@ -770,6 +776,11 @@ make_config! { /// Max burst size for login requests |> Allow a burst of requests of up to this size, while maintaining the average indicated by `login_ratelimit_seconds`. Note that this applies to both the login and the 2FA, so it's recommended to allow a burst size of at least 2 login_ratelimit_max_burst: u32, false, def, 10; + /// Seconds between unauthenticated requests |> Number of seconds, on average, between requests from the same IP address to any of the rate limited unauthenticated endpoints + unauthenticated_ratelimit_seconds: u64, false, def, 60; + /// Max burst size for unauthenticated requests |> Allow a burst of requests of up to this size, while maintaining the average indicated by `unauthenticated_ratelimit_seconds`. This is shared between several endpoints, so it needs to be more lenient than the login one + unauthenticated_ratelimit_max_burst: u32, false, def, 50; + /// Seconds between admin login requests |> Number of seconds, on average, between admin requests from the same IP address before rate limiting kicks in admin_ratelimit_seconds: u64, false, def, 300; /// Max burst size for admin login requests |> Allow a burst of requests of up to this size, while maintaining the average indicated by `admin_ratelimit_seconds` @@ -944,6 +955,18 @@ fn validate_config(cfg: &ConfigItems, on_update: bool) -> Result<(), Error> { } } + let trusted_proxies = cfg.ip_header_trusted_proxies.trim(); + if !trusted_proxies.eq_ignore_ascii_case("all") && !trusted_proxies.eq_ignore_ascii_case("local") { + for entry in trusted_proxies.split(',').filter(|e| !e.trim().is_empty()) { + if crate::auth::parse_trusted_proxy(entry).is_none() { + err!(format!( + "Invalid IP_HEADER_TRUSTED_PROXIES entry `{}`, expected an IP or CIDR range", + entry.trim() + )); + } + } + } + if cfg.password_iterations < 100_000 { err!("PASSWORD_ITERATIONS should be at least 100000 or higher. The default is 600000!"); } diff --git a/src/db/models/cipher.rs b/src/db/models/cipher.rs index 2fa6260a..eed5041d 100644 --- a/src/db/models/cipher.rs +++ b/src/db/models/cipher.rs @@ -42,13 +42,15 @@ pub struct Cipher { pub key: Option, - /* - Login = 1, - SecureNote = 2, - Card = 3, - Identity = 4, - SshKey = 5 - */ + // See (v2026.7.0): https://github.com/bitwarden/server/blob/5d4461aa42cadbacfef8fe2166c5453a5c52773a/src/Core/Vault/Enums/CipherType.cs + // Login = 1, + // SecureNote = 2, + // Card = 3, + // Identity = 4, + // SSHKey = 5 + // BankAccount = 6, + // DriversLicense = 7, + // Passport = 8, pub atype: i32, pub name: String, pub notes: Option, @@ -353,6 +355,9 @@ impl Cipher { "card": null, "identity": null, "sshKey": null, + "bankAccount": null, + "driversLicense": null, + "passport": null, }); // These values are only needed for user/default syncs @@ -392,6 +397,9 @@ impl Cipher { 3 => "card", 4 => "identity", 5 => "sshKey", + 6 => "bankAccount", + 7 => "driversLicense", + 8 => "passport", _ => err!(format!("Cipher {} has an invalid type {}", self.uuid, self.atype)), }; diff --git a/src/db/models/collection.rs b/src/db/models/collection.rs index f29843f7..8aec90ea 100644 --- a/src/db/models/collection.rs +++ b/src/db/models/collection.rs @@ -21,6 +21,7 @@ use super::{ User, UserId, }; +// See (v2026.7.0): https://github.com/bitwarden/server/blob/5d4461aa42cadbacfef8fe2166c5453a5c52773a/src/Core/AdminConsole/Entities/Collection.cs #[derive(Identifiable, Queryable, Insertable, AsChangeset)] #[diesel(table_name = collections)] #[diesel(treat_none_as_null = true)] @@ -71,6 +72,11 @@ impl Collection { "id": self.uuid, "organizationId": self.org_uuid, "name": self.name, + // Collection types are either 0: SharedCollection or 1: DefaultUserCollection, of which we do not yet support DefaultUserCollection. + // See (v2026.7.0): https://github.com/bitwarden/server/blob/5d4461aa42cadbacfef8fe2166c5453a5c52773a/src/Core/AdminConsole/Enums/CollectionType.cs + "type": 0, + // This is only used together with MyItems/DefaultUserCollection, which we do not yet support. + "defaultUserCollectionEmail": null, "object": "collection", }) } @@ -623,6 +629,47 @@ impl Collection { pub async fn is_manageable_by_user(&self, user_uuid: &UserId, conn: &DbConn) -> bool { Self::is_coll_manageable_by_user(&self.uuid, user_uuid, conn).await } + + // Whether the user has manage access to at least one collection in the org, directly or via a + // group. Org-scoped counterpart of is_coll_manageable_by_user. + pub async fn has_manageable_collection_by_user( + org_uuid: &OrganizationId, + user_uuid: &UserId, + conn: &DbConn, + ) -> bool { + let org_uuid = org_uuid.to_string(); + let user_uuid = user_uuid.to_string(); + conn.run(move |conn| { + collections::table + .left_join( + users_collections::table.on(users_collections::collection_uuid + .eq(collections::uuid) + .and(users_collections::user_uuid.eq(user_uuid.clone()))), + ) + .left_join( + users_organizations::table.on(collections::org_uuid + .eq(users_organizations::org_uuid) + .and(users_organizations::user_uuid.eq(user_uuid))), + ) + .left_join(groups_users::table.on(groups_users::users_organizations_uuid.eq(users_organizations::uuid))) + .left_join( + collections_groups::table.on(collections_groups::groups_uuid + .eq(groups_users::groups_uuid) + .and(collections_groups::collections_uuid.eq(collections::uuid))), + ) + .filter(collections::org_uuid.eq(&org_uuid)) + .filter( + // Manage permission on a collection assigned directly or via a group. + users_collections::manage.eq(true).or(collections_groups::manage.eq(true)), + ) + .count() + .first::(conn) + .ok() + .unwrap_or(0) + != 0 + }) + .await + } } /// Database methods diff --git a/src/db/models/group.rs b/src/db/models/group.rs index 820d3700..37037de6 100644 --- a/src/db/models/group.rs +++ b/src/db/models/group.rs @@ -271,7 +271,9 @@ impl Group { groups::table .inner_join(groups_users::table.on(groups_users::groups_uuid.eq(groups::uuid))) .inner_join( - users_organizations::table.on(users_organizations::uuid.eq(groups_users::users_organizations_uuid)), + users_organizations::table.on(users_organizations::uuid + .eq(groups_users::users_organizations_uuid) + .and(users_organizations::org_uuid.eq(groups::organizations_uuid))), ) .filter(users_organizations::user_uuid.eq(user_uuid)) .filter(groups::organizations_uuid.eq(org_uuid)) diff --git a/src/db/models/organization.rs b/src/db/models/organization.rs index 72b1df0b..bdb69864 100644 --- a/src/db/models/organization.rs +++ b/src/db/models/organization.rs @@ -217,11 +217,18 @@ impl Organization { "useSecretsManager": false, // Not supported (Not AGPLv3 Licensed) "selfHost": true, "useApi": true, + "useDisableSMAdsForUsers": true, // Hide Secrets Manager ads + "useInviteLinks": false, // Not (yet) supported + "useMyItems": false, // Not (yet) supported + "useOrganizationDomains": false, // Not supported (Linked to SSO) + "usePam": false, // Not supported + "usePhishingBlocker": false, "hasPublicAndPrivateKeys": self.private_key.is_some() && self.public_key.is_some(), "useResetPassword": CONFIG.mail_enabled(), "allowAdminAccessToAllCollectionItems": true, "limitCollectionCreation": true, "limitCollectionDeletion": true, + "limitItemDeletion": false, "businessName": self.name, "businessAddress1": null, @@ -495,6 +502,12 @@ impl Membership { "useActivateAutofillPolicy": false, "useAdminSponsoredFamilies": false, "useRiskInsights": false, // Not supported (Not AGPLv3 Licensed) + "useDisableSMAdsForUsers": true, // Hide Secrets Manager ads + "useInviteLinks": false, // Not (yet) supported + "useMyItems": false, // Not (yet) supported + "useOrganizationDomains": false, // Not supported (Linked to SSO) + "usePam": false, // Not supported + "usePhishingBlocker": false, "organizationUserId": self.uuid, "providerId": null, diff --git a/src/db/models/send.rs b/src/db/models/send.rs index 48159e8a..c5bc98c4 100644 --- a/src/db/models/send.rs +++ b/src/db/models/send.rs @@ -231,6 +231,53 @@ impl Send { } } + /// Registers an access, incrementing `access_count` only while below `max_access_count`. + /// Returns false when the limit was already reached. The check and the increment are a single + /// statement, otherwise concurrent accesses can both pass the check and exceed the limit. + pub async fn register_access(&mut self, conn: &DbConn) -> Result { + self.update_users_revision(conn).await; + + let revision_date = Utc::now().naive_utc(); + let uuid = self.uuid.clone(); + let updated = conn + .run(move |conn| { + diesel::update(sends::table) + .filter(sends::uuid.eq(uuid)) + .filter( + sends::max_access_count + .is_null() + .or(sends::access_count.nullable().lt(sends::max_access_count)), + ) + .set((sends::access_count.eq(sends::access_count + 1), sends::revision_date.eq(revision_date))) + .execute(conn) + }) + .await?; + + if updated == 0 { + return Ok(false); + } + + self.access_count += 1; + self.revision_date = revision_date; + Ok(true) + } + + /// Whether the Send is currently within its validity window: not disabled, not past its + /// expiration date, and not past its deletion date. Does not consider `max_access_count` + /// (consumed at token issuance) or the password. + pub fn is_accessible(&self) -> bool { + let now = Utc::now().naive_utc(); + if self.disabled { + return false; + } + if let Some(expiration) = self.expiration_date + && now >= expiration + { + return false; + } + now < self.deletion_date + } + pub async fn delete(&self, conn: &DbConn) -> EmptyResult { self.update_users_revision(conn).await; diff --git a/src/db/models/user.rs b/src/db/models/user.rs index 24bee751..93d750d5 100644 --- a/src/db/models/user.rs +++ b/src/db/models/user.rs @@ -268,8 +268,25 @@ impl User { UserStatus::Enabled }; + let account_keys = if self.private_key.is_some() { + json!({ + "publicKeyEncryptionKeyPair": { + "wrappedPrivateKey": self.private_key, + "publicKey": self.public_key, + "signedPublicKey": null, + "object": "publicKeyEncryptionKeyPair", + }, + "securityState": null, + "signatureKeyPair": null, + "object": "privateKeys" + }) + } else { + Value::Null + }; + json!({ "_status": status as i32, + "accountKeys": account_keys, "id": self.uuid, "name": self.name, "email": self.email, diff --git a/src/http_client.rs b/src/http_client.rs index 205b1cc3..0831d990 100644 --- a/src/http_client.rs +++ b/src/http_client.rs @@ -174,6 +174,27 @@ pub enum CustomHttpClientError { } impl CustomHttpClientError { + /// Attach the domain that resolved to this address, which `should_block_host()` can't know. + fn with_domain(self, name: &str) -> Self { + match self { + Self::NonGlobalIp { + ip, + .. + } => Self::NonGlobalIp { + domain: Some(name.to_owned()), + ip, + }, + Self::Blocked { + domain, + } => Self::Blocked { + domain: format!("{name} ({domain})"), + }, + other @ Self::Invalid { + .. + } => other, + } + } + pub fn downcast_ref(e: &dyn std::error::Error) -> Option<&Self> { let mut source = e.source(); @@ -285,14 +306,12 @@ fn pre_resolve(name: &str, enforce_block: bool) -> Result<(), CustomHttpClientEr } fn post_resolve(name: &str, ip: IpAddr) -> Result<(), CustomHttpClientError> { - if should_block_ip(ip) { - Err(CustomHttpClientError::NonGlobalIp { - domain: Some(name.to_owned()), - ip, - }) - } else { - Ok(()) - } + let host: Host<&str> = match ip { + IpAddr::V4(ip) => Host::Ipv4(ip), + IpAddr::V6(ip) => Host::Ipv6(ip), + }; + + should_block_host(&host).map_err(|e| e.with_domain(name)) } impl Resolve for CustomDns { diff --git a/src/main.rs b/src/main.rs index 15467ea4..28645694 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,7 +2,7 @@ // The recursion_limit is mainly triggered by the json!() macro. // The more key/value pairs there are the more recursion occurs. // We want to keep this as low as possible! -#![recursion_limit = "165"] +#![recursion_limit = "192"] // When enabled use MiMalloc as malloc instead of the default malloc #[cfg(feature = "enable_mimalloc")] diff --git a/src/ratelimit.rs b/src/ratelimit.rs index 2b422924..70217957 100644 --- a/src/ratelimit.rs +++ b/src/ratelimit.rs @@ -18,6 +18,24 @@ static LIMITER_ADMIN: LazyLock = LazyLock::new(|| { RateLimiter::keyed(Quota::with_period(seconds).expect("Non-zero admin ratelimit seconds").allow_burst(burst)) }); +static LIMITER_UNAUTHENTICATED: LazyLock = LazyLock::new(|| { + let seconds = Duration::from_secs(CONFIG.unauthenticated_ratelimit_seconds()); + let burst = NonZeroU32::new(CONFIG.unauthenticated_ratelimit_max_burst()) + .expect("Non-zero unauthenticated ratelimit burst"); + RateLimiter::keyed( + Quota::with_period(seconds).expect("Non-zero unauthenticated ratelimit seconds").allow_burst(burst), + ) +}); + +pub fn check_limit_unauthenticated(ip: &IpAddr) -> Result<(), Error> { + match LIMITER_UNAUTHENTICATED.check_key(ip) { + Ok(()) => Ok(()), + Err(_e) => { + err_code!("Too many requests", 429); + } + } +} + pub fn check_limit_login(ip: &IpAddr) -> Result<(), Error> { match LIMITER_LOGIN.check_key(ip) { Ok(()) => Ok(()), diff --git a/src/sso_client.rs b/src/sso_client.rs index ff39b0b0..bc766586 100644 --- a/src/sso_client.rs +++ b/src/sso_client.rs @@ -95,7 +95,9 @@ impl<'c> AsyncHttpClient<'c> for OidcHttpClient { } let body = response.bytes().await.map_err(Box::new)?; - debug!("Response body {}", String::from_utf8_lossy(&body)); + if CONFIG.sso_debug_tokens() { + debug!("Response body {}", String::from_utf8_lossy(&body)); + } builder.body(body.to_vec()).map_err(HttpClientError::Http) }) } diff --git a/src/static/templates/scss/vaultwarden.scss.hbs b/src/static/templates/scss/vaultwarden.scss.hbs index 477cdd34..5bbe5db2 100644 --- a/src/static/templates/scss/vaultwarden.scss.hbs +++ b/src/static/templates/scss/vaultwarden.scss.hbs @@ -116,8 +116,8 @@ app-security > app-two-factor-setup > form { } /* Hide unsupported Custom Role options */ -bit-dialog div.tw-ml-4:has(bit-form-control input), -bit-dialog div.tw-col-span-4:has(input[formcontrolname*="access"], input[formcontrolname*="manage"]) { +:is(bit-dialog, [bit-dialog]) div.tw-ml-4:has(bit-form-control input), +:is(bit-dialog, [bit-dialog]) div.tw-col-span-4:has(input[formcontrolname*="access"], input[formcontrolname*="manage"]) { @extend %vw-hide; }