VYPR
Moderate severityOSV Advisory· Published Jan 15, 2026· Updated Jan 15, 2026

Eclipse Vert.x Web static handler file access denial

CVE-2026-1002

Description

The Vert.x Web static handler component cache can be manipulated to deny the access to static files served by the handler using specifically crafted request URI.

The issue comes from an improper implementation of the C. rule of section 5.2.4 of RFC3986 and is fixed in Vert.x Core component (used by Vert.x Web): https://github.com/eclipse-vertx/vert.x/pull/5895

Steps to reproduce Given a file served by the static handler, craft an URI that introduces a string like bar%2F..%2F after the last / char to deny the access to the URI with an HTTP 404 response. For example https://example.com/foo/index.html can be denied with https://example.com/foo/bar%2F..%2Findex.html

Mitgation Disabling Static Handler cache fixes the issue.

StaticHandler staticHandler = StaticHandler.create().setCachingEnabled(false);

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
io.vertx:vertx-coreMaven
< 4.5.244.5.24
io.vertx:vertx-coreMaven
>= 5.0.0.CR1, < 5.0.75.0.7

Affected products

1

Patches

2
d007e7b41854

Fix a bug in the removeDots implementation.

https://github.com/eclipse-vertx/vert.xJulien VietJan 14, 2026via ghsa
2 files changed · +70 6
  • src/main/java/io/vertx/core/http/impl/HttpUtils.java+2 6 modified
    @@ -406,16 +406,12 @@ public static String removeDots(CharSequence path) {
             // preserve last slash
             i += 3;
             int pos = obuf.lastIndexOf("/");
    -        if (pos != -1) {
    -          obuf.delete(pos, obuf.length());
    -        }
    +        obuf.setLength(pos == -1 ? 0 : pos);
           } else if (matches(path, i, "/..", true)) {
             path = "/";
             i = 0;
             int pos = obuf.lastIndexOf("/");
    -        if (pos != -1) {
    -          obuf.delete(pos, obuf.length());
    -        }
    +        obuf.setLength(pos == -1 ? 0 : pos);
           } else if (matches(path, i, ".", true) || matches(path, i, "..", true)) {
             break;
           } else {
    
  • src/test/java/io/vertx/core/impl/HttpUtilsTest.java+68 0 added
    @@ -0,0 +1,68 @@
    +/*
    + * Copyright (c) 2011-2023 Contributors to the Eclipse Foundation
    + *
    + * This program and the accompanying materials are made available under the
    + * terms of the Eclipse Public License 2.0 which is available at
    + * http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
    + * which is available at https://www.apache.org/licenses/LICENSE-2.0.
    + *
    + * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
    + */
    +package io.vertx.core.impl;
    +
    +import io.vertx.core.http.impl.HttpUtils;
    +import org.junit.Test;
    +
    +import static org.junit.Assert.assertEquals;
    +
    +public class HttpUtilsTest {
    +
    +  @Test
    +  public void testRemoveDotSegmentsRuleA() {
    +    assertDotSegments("", "../");
    +    assertDotSegments("", "./");
    +
    +    assertDotSegments("foo", "../foo");
    +    assertDotSegments("foo", "./foo");
    +  }
    +
    +  @Test
    +  public void testRemoveDotSegmentsRuleB() {
    +    assertDotSegments("/", "/./");
    +    assertDotSegments("/", "/.");
    +
    +    assertDotSegments("/foo", "/./foo");
    +  }
    +
    +  @Test
    +  public void testRemoveDotSegmentsRuleC() {
    +    assertDotSegments("/", "/../");
    +    assertDotSegments("/foo", "/../foo");
    +    assertDotSegments("/", "/..");
    +    assertDotSegments("/", "/foo/../");
    +    assertDotSegments("/", "/foo/..");
    +    assertDotSegments("/", "foo/../");
    +    assertDotSegments("/", "foo/..");
    +    assertDotSegments("/foo/", "/foo/bar/../");
    +    assertDotSegments("/foo/", "/foo/bar/..");
    +    assertDotSegments("foo/", "foo/bar/../");
    +    assertDotSegments("foo/", "foo/bar/..");
    +  }
    +
    +  @Test
    +  public void testRemoveDotSegmentsRuleD() {
    +    assertDotSegments("", ".");
    +    assertDotSegments("", "..");
    +  }
    +
    +  @Test
    +  public void testRemoveDotSegmentsRuleE() {
    +    assertDotSegments("/foo", "/foo");
    +    assertDotSegments("foo", "foo");
    +  }
    +
    +  private static void assertDotSegments(String expected, String test) {
    +    String actual = HttpUtils.removeDots(test);
    +    assertEquals(expected, actual);
    +  }
    +}
    
5b67f5d17788

Fix a bug in the removeDotSegments implementation.

https://github.com/eclipse-vertx/vert.xJulien VietJan 13, 2026via ghsa
2 files changed · +71 6
  • vertx-core/src/main/java/io/vertx/core/internal/net/RFC3986.java+2 6 modified
    @@ -157,16 +157,12 @@ public static String removeDotSegments(CharSequence path) {
             // preserve last slash
             i += 3;
             int pos = obuf.lastIndexOf("/");
    -        if (pos != -1) {
    -          obuf.delete(pos, obuf.length());
    -        }
    +        obuf.setLength(pos == -1 ? 0 : pos);
           } else if (matches(path, i, "/..", true)) {
             path = "/";
             i = 0;
             int pos = obuf.lastIndexOf("/");
    -        if (pos != -1) {
    -          obuf.delete(pos, obuf.length());
    -        }
    +        obuf.setLength(pos == -1 ? 0 : pos);
           } else if (matches(path, i, ".", true) || matches(path, i, "..", true)) {
             break;
           } else {
    
  • vertx-core/src/test/java/io/vertx/tests/net/RFC3986Test.java+69 0 added
    @@ -0,0 +1,69 @@
    +/*
    + * Copyright (c) 2011-2026 Contributors to the Eclipse Foundation
    + *
    + * This program and the accompanying materials are made available under the
    + * terms of the Eclipse Public License 2.0 which is available at
    + * http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
    + * which is available at https://www.apache.org/licenses/LICENSE-2.0.
    + *
    + * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
    + */
    +package io.vertx.tests.net;
    +
    +import io.vertx.core.internal.net.RFC3986;
    +import org.junit.ComparisonFailure;
    +import org.junit.Test;
    +
    +import static org.junit.Assert.assertEquals;
    +
    +public class RFC3986Test {
    +
    +  @Test
    +  public void testRemoveDotSegmentsRuleA() {
    +    assertDotSegments("", "../");
    +    assertDotSegments("", "./");
    +
    +    assertDotSegments("foo", "../foo");
    +    assertDotSegments("foo", "./foo");
    +  }
    +
    +  @Test
    +  public void testRemoveDotSegmentsRuleB() {
    +    assertDotSegments("/", "/./");
    +    assertDotSegments("/", "/.");
    +
    +    assertDotSegments("/foo", "/./foo");
    +  }
    +
    +  @Test
    +  public void testRemoveDotSegmentsRuleC() {
    +    assertDotSegments("/", "/../");
    +    assertDotSegments("/foo", "/../foo");
    +    assertDotSegments("/", "/..");
    +    assertDotSegments("/", "/foo/../");
    +    assertDotSegments("/", "/foo/..");
    +    assertDotSegments("/", "foo/../");
    +    assertDotSegments("/", "foo/..");
    +    assertDotSegments("/foo/", "/foo/bar/../");
    +    assertDotSegments("/foo/", "/foo/bar/..");
    +    assertDotSegments("foo/", "foo/bar/../");
    +    assertDotSegments("foo/", "foo/bar/..");
    +  }
    +
    +  @Test
    +  public void testRemoveDotSegmentsRuleD() {
    +    assertDotSegments("", ".");
    +    assertDotSegments("", "..");
    +  }
    +
    +  @Test
    +  public void testRemoveDotSegmentsRuleE() {
    +    assertDotSegments("/foo", "/foo");
    +    assertDotSegments("foo", "foo");
    +  }
    +
    +  private static void assertDotSegments(String expected, String test) {
    +    String actual = RFC3986.removeDotSegments(test);
    +    assertEquals(expected, actual);
    +  }
    +}
    

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

News mentions

0

No linked articles in our index yet.