CVE-2026-28807
Description
Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal') vulnerability in gleam-wisp wisp allows arbitrary file read via percent-encoded path traversal.
The wisp.serve_static function is vulnerable to path traversal because sanitization runs before percent-decoding. The encoded sequence %2e%2e passes through string.replace unchanged, then uri.percent_decode converts it to .., which the OS resolves as directory traversal when the file is read.
An unauthenticated attacker can read any file readable by the application process in a single HTTP request, including application source code, configuration files, secrets, and system files.
This issue affects wisp: from 2.1.1 before 2.2.1.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
wispHex | >= 2.1.1, < 2.2.1 | 2.2.1 |
Affected products
1Patches
24 files changed · +29 −15
CHANGELOG.md+4 −0 modified@@ -6,6 +6,10 @@ package. A `wisp_ewe` package has been created too! - `send_file` failing will now result in an internal server error being sent. +## v2.2.1 - 2026-03-08 + +- Fixed a path traversal attack with encoded URI paths in `serve_static`. + ## v2.2.0 - 2026-01-16 - The `content_security_policy_protection` middleware has been added.
gleam.toml+1 −1 modified@@ -1,5 +1,5 @@ name = "wisp" -version = "2.2.0" +version = "2.2.1" gleam = ">= 1.11.0" description = "A practical web framework for Gleam" licences = ["Apache-2.0"]
src/wisp.gleam+2 −5 modified@@ -1423,14 +1423,11 @@ pub fn serve_static( let path = path |> string.drop_start(string.length(prefix)) + |> uri.percent_decode + |> result.unwrap(path) |> string.replace(each: "..", with: "") |> filepath.join(directory, _) - let path = case uri.percent_decode(path) { - Ok(p) -> p - Error(_) -> path - } - let file_type = req.path |> string.split(on: ".")
test/wisp_test.gleam+22 −9 modified@@ -503,20 +503,33 @@ pub fn serve_static_not_found_test() { |> request.set_path("/stuff/credit_card_details.txt") assert { use <- wisp.serve_static(request, under: "/stuff", from: "./") - wisp.ok() + wisp.not_found() } - == wisp.ok() + == wisp.not_found() } -pub fn serve_static_go_up_test() { +pub fn serve_static_path_traversal_attack_test() { let request = simulate.request(http.Get, "/") - |> request.set_path("/../test/fixture.txt") - assert { - use <- wisp.serve_static(request, under: "/stuff", from: "./src/") - wisp.ok() - } - == wisp.ok() + |> request.set_path("/../gleam.toml") + let response = { + use <- wisp.serve_static(request, under: "/", from: "./src/") + wisp.not_found() + } + assert response == wisp.not_found() + as "the file should not be returned as the .. segment should be discarded to prevent path traversals" +} + +pub fn serve_static_path_traversal_attack_uri_encoded_test() { + let request = + simulate.request(http.Get, "/") + |> request.set_path("/%2e%2e/gleam.toml") + let response = { + use <- wisp.serve_static(request, under: "/", from: "./src/") + wisp.not_found() + } + assert response == wisp.not_found() + as "the file should not be returned as the .. segment should be discarded to prevent path traversals" } pub fn serve_static_etags_returns_304_test() {
1 file changed · +5 −0
src/wisp.gleam+5 −0 modified@@ -1425,6 +1425,11 @@ pub fn serve_static( |> string.drop_start(string.length(prefix)) |> string.replace(each: "..", with: "") |> filepath.join(directory, _) + + let path = case uri.percent_decode(path) { + Ok(p) -> p + Error(_) -> path + } let file_type = req.path
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
7- github.com/advisories/GHSA-h7cj-j2vv-qw8rghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2026-28807ghsaADVISORY
- cna.erlef.org/cves/CVE-2026-28807.htmlnvdWEB
- github.com/gleam-wisp/wisp/commit/129dcb1fe10ab1e676145d91477535e1c90ab550ghsaWEB
- github.com/gleam-wisp/wisp/commit/161118c431047f7ef1ff7cabfcc38981877fdd93nvdWEB
- github.com/gleam-wisp/wisp/security/advisories/GHSA-h7cj-j2vv-qw8rnvdWEB
- osv.dev/vulnerability/EEF-CVE-2026-28807nvdWEB
News mentions
0No linked articles in our index yet.