Bug 61201 - CGIServlet adds too much to the SCRIPT_NAME environment variable if script followed by extra path
Summary: CGIServlet adds too much to the SCRIPT_NAME environment variable if script fo...
Status: RESOLVED FIXED
Alias: None
Product: Tomcat 9
Classification: Unclassified
Component: Catalina (show other bugs)
Version: 9.0.0.M21
Hardware: PC Linux
: P2 normal (vote)
Target Milestone: -----
Assignee: Tomcat Developers Mailing List
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2017-06-19 21:53 UTC by jm009
Modified: 2017-09-06 09:22 UTC (History)
0 users



Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description jm009 2017-06-19 21:53:51 UTC
I run the following bash script as CGI (test2.sh):

-----
#!/bin/bash

echo "Content-Type: text/plain"
echo

set
-----

If I call it as

http://127.0.0.1:8086/nextcloud/test2.sh

it outputs

[...]
SCRIPT_NAME=/nextcloud/test2.sh
[...]

If I call it as

http://127.0.0.1:8086/nextcloud/test2.sh/login

it outputs

[...]
SCRIPT_NAME=/nextcloud/test2.sh/login/test2.sh
[...]


But the value of $SCRIPT_NAME should stay the same.

Excerpt from my web.xml:

   <servlet>
      <servlet-name>test-cgi</servlet-name>
      <servlet-class>org.apache.catalina.servlets.CGIServlet</servlet-class>
      <init-param>
        <param-name>executable</param-name>
        <param-value>/bin/bash</param-value>
      </init-param>
      <init-param>
        <param-name>passShellEnvironment</param-name>
        <param-value>true</param-value>
      </init-param>
   </servlet>

   <servlet-mapping>
      <servlet-name>test-cgi</servlet-name>
      <url-pattern>*.sh</url-pattern>
    </servlet-mapping>

   <servlet-mapping>
      <servlet-name>test-cgi</servlet-name>
      <url-pattern>/test2.sh/login</url-pattern>
    </servlet-mapping>
Comment 1 Mark Thomas 2017-06-20 19:07:29 UTC
Hmm. The CGI servlet isn't designed to be mapped that way. I'm a little surprised it even worked.

The docs aren't clear on what is expected to work and what isn't.

The script finding logic appears depend on what sort of mapping is used. The new getHttpServletMapping() in Servlet 4.0 may enable a wider range of mappings to be supported.

I need to dig into this some more. At the moment, the minimum I anticipate doing is:
- documented which mapping styles are supported and which are not
- updating the checks in 9.0.x (and 8.5.x since the Servlet 4.0 functionality is back-ported) to reject requests using unsupported mapping types.

At best, I'll add support for all mapping types and document each.
Comment 2 Mark Thomas 2017-06-20 21:13:08 UTC
That turned out much better than I expected. The fix was simple and did not need to depend on the new Servlet 4.0 features. Best of all, it used LESS code.

Fixed in:
- trunk for 9.0.0.M22 onwards
- 8.5.x for 8.5.16 onwards
- 8.0.x for 8.0.45 onwards
- 7.0.x for 7.0.79 onwards
Comment 3 jm009 2017-09-05 13:44:26 UTC
It seems, that the fix disables calling

http://127.0.0.1:8086/nextcloud/test2.sh/login


It gives:

HTTP Status [404] – [Not Found]

Type Status Report

Description The origin server did not find a current representation for the target resource or is not willing to disclose that one exists.
Comment 4 jm009 2017-09-06 08:46:09 UTC
Sorry, it works.
I had changed my web.xml.
Comment 5 jm009 2017-09-06 09:22:14 UTC
What does not work is

   <servlet-mapping>
      <servlet-name>test-cgi</servlet-name>
      <url-pattern>/test2.sh/*</url-pattern>
    </servlet-mapping>

This would be usefull for nextcloud.
For demo see demo.nextcloud.com

It uses virtual paths like

/login
/apps/files
/apps/activity
/apps/gallery

(At least I think they are virtual, because there is no index.php in the /apps/files, /apps/activity and /apps/gallery subdirectories. And there is no /login subdirectory.)


In lib/base.php in function handleRequest() there are checks like

if ($requestPath === '/heartbeat') {

if ($requestPath === '/disableapp'
                        && $request->getMethod() === 'POST'
                        && ((array)$request->getParam('appid')) !== ''
                ) {

I'll see if I can get it to work otherwise.


The sqwebmail-servlet even puts some arbitrary long numbers in the path part of the URL.