Skip to content

fix(deps): update rust crate salvo to 0.88.0 [security]#122

Open
renovate[bot] wants to merge 1 commit intomainfrom
renovate/crate-salvo-vulnerability
Open

fix(deps): update rust crate salvo to 0.88.0 [security]#122
renovate[bot] wants to merge 1 commit intomainfrom
renovate/crate-salvo-vulnerability

Conversation

@renovate
Copy link
Contributor

@renovate renovate bot commented Jan 8, 2026

ℹ️ Note

This PR body was truncated due to platform limits.

This PR contains the following updates:

Package Type Update Change
salvo (source) dependencies minor 0.37.90.88.0

GitHub Vulnerability Alerts

CVE-2026-22257

Summary

The function list_html generates a file view of a folder without sanitizing the files or folders names, potentially leading to XSS in cases where a website allows access to public files using this feature, allowing anyone to upload a file.

Details

The vulnerable snippet of code is the following:
dir.rs

// ... fn list_html(...
        let mut link = "".to_owned();
        format!(
            r#"<a href="/">{}</a>{}"#,
            HOME_ICON,
            segments
                .map(|seg| {
                    link = format!("{link}/{seg}");
                    format!("/<a href=\"{link}\">{seg}</a>")
                })
                .collect::<Vec<_>>()
                .join("")
        )
// ...

PoC

POC1.mp4

Here is the example app we used:

mian.rs

use salvo::prelude::*;
use salvo::serve_static::StaticDir;
use std::path::PathBuf;
use tokio::fs;

const INDEX_HTML: &str = r#"<!doctype html>
<html>
  <head><meta charset="utf-8"><title>StaticDir PoC</title></head>
  <body>
    <h2>Upload a file</h2>
    <form action="/upload" method="post" enctype="multipart/form-data">
      <input type="file" name="file" />
      <button type="submit">Upload</button>
    </form>

    <p>Browse uploads:</p>
    <ul>
      <li><a href="/files">/files</a></li>
      <li><a href="/files/">/files/</a></li>
    </ul>
  </body>
</html>
"#;

#[handler]
async fn index(res: &mut Response) {
    res.render(Text::Html(INDEX_HTML));
}

#[handler]
async fn upload(req: &mut Request, res: &mut Response) {
    fs::create_dir_all("uploads").await.expect("create uploads dir");

    let form = match req.form_data().await {
        Ok(v) => v,
        Err(e) => {
            res.status_code(StatusCode::BAD_REQUEST);
            res.render(Text::Plain(format!("form_data parse failed: {e}")));
            return;
        }
    };

    let Some(file_part) = form.files.get("file") else {
        res.status_code(StatusCode::BAD_REQUEST);
        res.render(Text::Plain("missing file field (name=\"file\")"));
        return;
    };

    let original_name = file_part.name().unwrap_or("upload.bin");

    let mut dest = PathBuf::from("uploads");
    dest.push(original_name);

    let tmp_path = file_part.path();
    if let Err(e) = fs::copy(tmp_path, &dest).await {
        res.status_code(StatusCode::INTERNAL_SERVER_ERROR);
        res.render(Text::Plain(format!("save failed: {e}")));
        return;
    }

    res.render(Text::Plain(format!(
        "Uploaded as: {original_name}\nNow open: http://127.0.0.1:5800/files/\n"
    )));
}

#[tokio::main]
async fn main() {
    tracing_subscriber::fmt().init();
    fs::create_dir_all("uploads").await.expect("create uploads dir");

    let router = Router::new()
        .get(index)
        .push(Router::with_path("upload").post(upload))
        .push(
            Router::with_path("files/{**rest_path}")
                .get(StaticDir::new("uploads").auto_list(true)),
        );

    let acceptor = TcpListener::new("127.0.0.1:5800").bind().await;
    Server::new(acceptor).serve(router).await;
}

Cargo.toml

[package]
name = "poc"
version = "0.1.0"
edition = "2024"

[dependencies]
salvo = { version = "0.85.0", features = ["serve-static"] }
tokio = { version = "1", features = ["macros", "rt-multi-thread", "fs"] }
tracing-subscriber = "0.3"

Impact

JavaScript execution, most likely leading to an account takeover, depending on the site's constraint (CSP, etc…).

CVE-2026-22256

Summary

The function list_html generates an file view of a folder which includes a render of the current path, in which its inserted in the HTML without proper sanitation, leading to reflected XSS. The request path is decoded and normalized in the matching stage but is not inserted raw in the HTML view (current.path). The only constraint here is for the root path (e.g., /files in the PoC example) to have a subdirectory (e. g., common ones like styles/scripts/etc.) so that the matching returns the list HTML page instead of the Not Found page.

Details

The vulnerable snippet of code is the following:
dir.rs

// ... fn list_html(...
    let mut ftxt = format!(
        r#"<!DOCTYPE html><html><head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width">
        <title>{}</title>
        <style>{}</style></head><body><header><h3>Index of: {}</h3></header><hr/>"#,
        current.path,
        HTML_STYLE,
        header_links(&current.path)
    );
// ...

As seen here <title>{}</title> it is inserted unsafely.

PoC

salvo_poc.mp4

Here is the example app, note this doesn’t need an upload feature (e.g to the other reported vulnerability), only the sub-folder is required.

main.rs

use salvo::prelude::*;
use salvo::serve_static::StaticDir;
use tokio::fs;

#[tokio::main]
async fn main() {
    tracing_subscriber::fmt().init();
    fs::create_dir_all("uploads").await.expect("create uploads dir");

    let router = Router::new()
        .push(
            Router::with_path("files/{**rest_path}")
                .get(StaticDir::new("uploads").auto_list(true)),
        );

    let acceptor = TcpListener::new("127.0.0.1:5800").bind().await;
    Server::new(acceptor).serve(router).await;
}

Cargo.toml

[package]
name = "salvo-staticdir-xss-poc"
version = "0.1.0"
edition = "2024"

[dependencies]
salvo = { version = "0.85.0", features = ["serve-static"] }
tokio = { version = "1", features = ["macros", "rt-multi-thread", "fs"] }
tracing-subscriber = "0.3"

Setup commands:

mkdir uploads
mkdir uploads/bla

Impact

JavaScript execution, most likely leading to an account takeover, depending on the site's constraint (CSP, etc…).


Release Notes

salvo-rs/salvo (salvo)

v0.88.1

Compare Source

What's Changed

Full Changelog: salvo-rs/salvo@v0.88.0...v0.88.1

v0.88.0

Compare Source

What's Changed

Full Changelog: salvo-rs/salvo@v0.87.1...v0.88.0

v0.87.1

Compare Source

What's Changed

Full Changelog: salvo-rs/salvo@v0.87.0...v0.87.1

v0.87.0

Compare Source

What's Changed

New Contributors

Full Changelog: salvo-rs/salvo@v0.86.0...v0.87.0

v0.86.0

Compare Source

What's Changed

New Contributors

Full Changelog: salvo-rs/salvo@v0.85.0...v0.86.0

v0.85.0

Compare Source

What's Changed

New Contributors

Full Changelog: salvo-rs/salvo@v0.84.2...v0.85.0

v0.84.2

Compare Source

What's Changed

Full Changelog: salvo-rs/salvo@v0.84.1...v0.84.2

v0.84.1

Compare Source

What's Changed

Full Changelog: salvo-rs/salvo@v0.84.0...v0.84.1

v0.84.0

Compare Source

What's Changed

Full Changelog: salvo-rs/salvo@v0.83.0...v0.84.0

v0.83.0

Compare Source

What's Changed

Full Changelog: salvo-rs/salvo@v0.82.0...v0.83.0

v0.82.0

Compare Source

What's Changed

New Contributors

Full Changelog: salvo-rs/salvo@v0.81.0...v0.82.0

v0.81.0

Compare Source

What's Changed

Full Changelog: salvo-rs/salvo@v0.80.0...v0.81.0

v0.80.0

Compare Source

What's Changed

New Contributors

Full Changelog: salvo-rs/salvo@v0.79.0...v0.80.0

v0.79.0

Compare Source

What's Changed

New Contributors

Full Changelog: salvo-rs/salvo@v0.78.0...v0.79.0

v0.78.0

Compare Source

What's Changed

New Contributors

Full Changelog: salvo-rs/salvo@v0.77.1...v0.78.0

v0.77.1

Compare Source

What's Changed

Full Changelog: salvo-rs/salvo@v0.77.0...v0.77.1

v0.77.0

Compare Source

What's Changed

New Contributors

Full Changelog: salvo-rs/salvo@v0.76.2...v0.77.0

v0.76.2

Compare Source

What's Changed

New Contributors

Full Changelog: salvo-rs/salvo@v0.76.1...v0.76.2

v0.76.1

Compare Source

What's Changed

New Contributors

Full Changelog: salvo-rs/salvo@v0.76.0...v0.76.1

v0.76.0

Compare Source

What's Changed

New Contributors

Full Changelog: salvo-rs/salvo@v0.75.0...v0.76.0

v0.75.0

Compare Source

What's Changed

New Contributors

Full Changelog: salvo-rs/salvo@v0.74.3...v0.75.0

v0.74.3

Compare Source

What's Changed

New Contributors

Full Changelog: salvo-rs/salvo@v0.74.2...v0.74.3

v0.74.2

Compare Source

What's Changed

New Contributors

Full Changelog: salvo-rs/salvo@v0.74.1...v0.74.2

v0.74.1

Compare Source

What's Changed

New Contributors

Full Changelog: salvo-rs/salvo@v0.74.0...v0.74.1

v0.74.0

Compare Source

What's Changed

New Contributors

Full Changelog: salvo-rs/salvo@v0.73.0...v0.74.0

v0.73.0

Compare Source

What's Changed

Full Changelog: salvo-rs/salvo@v0.72.4...v0.73.0

v0.72.4

Compare Source

What's Changed

Full Changelog: salvo-rs/salvo@v0.72.3...v0.72.4

v0.72.3

Compare Source

What's Changed

New Contributors

Full Changelog: salvo-rs/salvo@v0.72.2...v0.72.3

v0.72.2

Compare Source

What's Changed

New Contributors

Full Changelog: salvo-rs/salvo@v0.72.1...v0.72.2

v0.72.1

Compare Source

What's Changed


Configuration

📅 Schedule: Branch creation - "" (UTC), Automerge - At any time (no schedule defined).

🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.

Rebasing: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.

🔕 Ignore: Close this PR and you won't be reminded about this update again.


  • If you want to rebase/retry this PR, check this box

This PR was generated by Mend Renovate. View the repository job log.

@renovate renovate bot force-pushed the renovate/crate-salvo-vulnerability branch from 45f2a67 to 3ce7b01 Compare February 2, 2026 15:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants