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)