You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@struts.apache.org by "Lukasz Lenart (Jira)" <ji...@apache.org> on 2026/08/23 18:29:00 UTC
[jira] [Created] (WW-5688) RestActionMapper maps URIs with an id into the empty namespace, so actions declared at namespace "/" 404
Lukasz Lenart created WW-5688:
---------------------------------
Summary: RestActionMapper maps URIs with an id into the empty namespace, so actions declared at namespace "/" 404
Key: WW-5688
URL: https://issues.apache.org/jira/browse/WW-5688
Project: Struts 2
Issue Type: Bug
Components: Plugin - REST
Affects Versions: 2.1.2
Reporter: Lukasz Lenart
Fix For: 7.4.0
Split out of [WW-2820|https://issues.apache.org/jira/browse/WW-2820], a 2008 umbrella of five unrelated REST plugin claims. This is the only one of the five that still reproduces on {{main}}. Originally reported by Alvin Singh.
h3. Symptom
When a package is declared at {{namespace="/"}}, REST URIs that carry an id are mapped into a _different_ namespace than those that do not. The result is that {{index}} works while {{show}} / {{update}} / {{destroy}} return 404 for the very same action.
Given:
[package name="rest-root" namespace="/" extends="rest-default"]
[action name="dog" class="com.example.DogAction"/]
[/package]
||Request||namespace from RestActionMapper||action resolves?||
|{{GET /dog}}|{{/}}|yes|
|{{GET /dog/1}}|(empty)|*no - 404*|
The reporter's original phrasing was "if you want a blank namespace, do not use / as your namespace ... inconsistent behavior starts to occur", which is accurate.
h3. Root cause
{{RestActionMapper.parseNameAndNamespace()}} has two disjoint paths:
* {{lastSlash == 0}} hardcodes {{namespace = "/"}} (the WW-1046 root case).
* Any deeper URI instead runs the longest-matching-namespace loop. That loop's guard, {{prefix.charAt(ns.length()) == '/'}}, can never select {{"/"}} - it would require a {{//}} prefix - so it falls through to its default of {{""}}.
{{RuntimeConfiguration.getActionConfig()}} then only fails over {{"/"}} to {{""}}, never {{""}} to {{"/"}} (see {{DefaultConfiguration.shouldFallbackToEmptyNamespace}}). So the {{""}} branch is a dead end for an action declared at {{"/"}}.
Note that the loop computes {{name = uri.substring(namespace.length() + 1)}}, which assumes a non-root namespace. Simply allowing {{"/"}} to match in the loop is therefore not sufficient on its own.
h3. Reproduction
Against a configuration holding only the package above:
PROBE index -> ns='/' name='dog'
PROBE show -> ns='' name='dog'
junit.framework.AssertionFailedError:
GET /dog/1 must resolve to the action declared in namespace "/"
The companion assertion for {{GET /dog}} passes against the same configuration, so the failure is specific to the id-bearing URI and not an artefact of the fixture.
h3. Security assessment
Not a security issue. The defect makes actions _less_ reachable rather than more, and aligning {{/dog/1}} with what {{/dog}} already resolves to exposes no surface that the {{/dog}} path does not already expose.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)