Loading...
Skip to main content

CVE-2026-22257 — Salvo is vulnerable to stored XSS in salvo — CVE...

HIGH

Salvo is vulnerable to stored XSS in the list_html function by uploading files with malicious names

Published January 8, 2026Updated February 3, 2026Source: osv

Summary

# 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**](https://github.com/salvo-rs/salvo/blob/16efeba312a274739606ce76366d921768628654/crates/serve-static/src/dir.rs#L581) ```rust // ... 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 https://github.com/user-attachments/assets/1e161e17-f033-4cc4-855b-43fd38ed1be4 Here is the example app we used: `mian.rs` ```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` ```rs [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…).

Advisory Timeline

  • First published January 8, 2026 (277 days ago).
  • Last revised February 3, 2026 — 25 days after initial publication.

Remediation

Upgrade to the fixed version using your package manager.

Cargo
Update salvo to 0.88.1 or later
cargo update -p salvo --precise 0.88.1

After upgrading, run your dependency scanner again to confirm the vulnerability is resolved.

Affected Packages (1)

Affects 1 ecosystem: crates.io (1).

PackageEcosystemAffectedFixed In
salvo
crates.io
All versions0.88.1

Vulnerability Classification

Common Weakness Enumeration (CWE) identifiers for this vulnerability type.

  • CWE-79
    Cross-site Scripting (XSS)MITRE

CVSS Score Breakdown

What the CVSS (Common Vulnerability Scoring System) 8.8 score means for each attack dimension.

Attack Vector
Network
Attack Complexity
Low
Privileges Required
None
User Interaction
Required
Scope
Changed
Confidentiality
High
Integrity
Low
Availability
Low

CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:L/A:L

Frequently Asked Questions

What is CVE-2026-22257?
Salvo is vulnerable to stored XSS in the list_html function by uploading files with malicious names This vulnerability has been assigned a severity rating of HIGH (CVSS score: 8.8/10).
How do I check if my project is affected by CVE-2026-22257?
CVE-2026-22257 affects salvo. Use GeekWala's free vulnerability scanner to check your dependencies against CVE-2026-22257 and 200,000+ other known vulnerabilities.

Severity & Exploitability

CVSS Score
8.8

High exploitability or significant impact. Prioritize remediation within days.

Also Known As

GHSA-54m3-5fxr-2f3j

Related CVEs

  • CVE-2026-93657
    HIGH

    hickory-resolver: Resolver::lookup() and Resolver::lookup_ip() APIs obscure DNSSEC validation failures

  • CVE-2026-18140
    HIGH

    aws-smithy-json: Uncontrolled recursion in the aws-smithy-json unknown-key skip path allows unauthenticated remote denial of service in smithy-rs generated servers

  • CVE-2026-33242
    HIGH

    Salvo has a Path Traversal in salvo-proxy::encode_url_path allows API Gateway Bypass

  • CVE-2026-33241
    HIGH

    Salvo Affected by Denial of Service via Unbounded Memory Allocation in Form Data Parsing

  • CVE-2026-22256
    HIGH

    Salvo is vulnerable to reflected XSS in the list_html function

Check if you're affected

Scan your dependencies to see if this vulnerability affects your projects.

Scan Your Dependencies