[Chapter 12] 12.5 CGI Lint--A Debugging/Testing Tool
Author: Shishir Gundavaram
Date: Tue Aug 26 16:44:13 1997

#

| Chapter 12 Debugging and Testing CGI Applications |

## 12.5 CGI Lint--A Debugging/Testing Tool

CGI Lint greatly simplifies the process of testing and debugging CGI applications. Appendix E, Applications, Modules, Utilities, and Documentation, lists where you can get CGI Lint.

Depending on the type of request (either GET or POST), either one or two auxiliary files are required by CGI Lint. The first is a configuration file, which should contain a list of the environment variables in the following format:

    REQUEST_METHOD     =   GET
    QUERY_STRING       =   name=John Surge&company=ABC Corporation!
    HTTP_ACCEPT        =   image/gif, image/x-xbitmap, image/jpeg, */*
    SERVER_PROTOCOL    =   HTTP/1.0
    REMOTE_ADDR        =   198.198.198.198
    SERVER_ROOT        =   /usr/local/bin/httpd_1.4.2
    DOCUMENT_ROOT      =   /usr/local/bin/httpd_1.4.2/public
    GATEWAY_INTERFACE  =   CGI/1.1
    SCRIPT_NAME        =   /cgi-bin/abc.pl
    SERVER_SOFTWARE    =   NCSA/1.4.2
    REMOTE_HOST        =   gateway.cgi.com

This format has an advantage over the previous one: You do not need to encode the query string. However, if you have either %, &, or = characters in the query string, you need to escape them by placing a "\" before them:

    QUERY_STRING       =   name=Joe\=Joseph&company=JP \& Play&percentage=50\%

Or you can just use the encoded values of %25, %26, and %3d to represent the "%," "&," and "=" characters, respectively. Now, you are ready to test out your CGI program:

    % CGI_Lint get.cfg

CGI Lint executes the script that is pointed to by the environment variables SCRIPT_NAME and SERVER_ROOT. In addition, you can use a data file to store query information. Here is an example:

    % CGI_Lint form.cfg form.data

The format for the data file should be:

    name = Joe\=Joseph
    company = JP \& Play
    percentage = 50\%

If you already have data stored in QUERY_STRING, CGI Lint will process the data from both sources. In the case of POST requests, all you have to do is change the REQUEST_METHOD to "POST" and run it in the same exact way as before:

    % CGI_Lint form.cfg form.data

In addition, you can test the multipart/form-data encoding scheme (see Appendix D, CGI Lite), which is a new addition to the Web. For multipart MIME data, you need to add the following line to the configuration file:

    CONTENT_TYPE = multipart/form-data

Normally, multipart data contains boundary strings between fields, but you do not have to go to the trouble of inserting the numerous multipart headers. CGI Lint takes care of all that for you. Now, here is the format for the data file:

    name = Joe = Joseph
    company = JP & Play
    percentage = 50%
    review = */usr/shishir/rev.dat

You would execute the script in the same way as you did all the others. CGI Lint reads through the fields and creates a multipart MIME body:

    -----------------------------78198732381
    Content-disposition: form-data; name="name"
    Joe = Joseph
    -----------------------------78198732381
    Content-disposition: form-data; name="company"
    JP & Play
    -----------------------------78198732381
    Content-disposition: form-data; name="percentage"
    50%
    -----------------------------78198732381
    Content-disposition: form-data; name="review"; filename="/usr/ shishir/rev.dat"
    .
    .
    (contents of the file /home/shishir/rev.dat)
    .
    .
    -----------------------------78198732381--

One thing to note here is the last line of the data file. The asterisk instructs the tool to include the information stored in the file /usr/shishir/review.dat. That is one of the powerful features of multipart messages: it allows users to upload files to the server.

In addition to simulating the server data streams, CGI Lint also checks a number of attributes and properties before running the script.

### CGI Lint in Action

Let's take a simple CGI program and run it through CGI Lint, and see what happens. Here is the program-it should be familiar to you, as it was introduced at the end of Chapter 7, Advanced Form Applications:

    #!/usr/local/bin/perl
    &parse_form_data(*simple);
    $user = $simple{'user'};
    print "Content-type: text/plain", "\n\n";
    print "Here are the results of your query: ", "\n";
    print `/usr/ucb/finger $user`;
    print "\n";
    exit (0);

This program outputs finger information about the specified user. Here is the form that is associated with the program:

    <FORM ACTION="/cgi-bin/finger.pl" METHOD="POST">
    <INPUT TYPE="text" NAME="user" SIZE=40>
    <INPUT TYPE="submit" VALUE="Get Information">
    </FORM>

Now, let's create the configuration and data files, to be used with CGI Lint. The configuration file must contain the following lines:

    REQUEST_METHOD = POST
    SERVER_ROOT = /usr/local/bin/httpd_1.4.2
        SCRIPT_NAME = /cgi-bin/finger.pl

Since the form passes the information to the program using POST, we need to create a data file to hold the post data. It needs to consist of only one line:

    user = shishir

This is equivalent to the user entering "shishir" in the user field in the form. That is all that needs to be done. Here is how you would execute CGI Lint (assuming that the configuration file is called finger.cfg, and the data file is called finger.dat):

    % CGI_Lint finger.cfg finger.dat

CGI Lint will output the following information:

    While looking at your Perl script for possible security holes and
    "open" commands, I came across the following statements that *might*
    constitute a security breach:
    ================================================================================
    Check the *backtics* on line: print `/usr/ucb/finger $user`;
    Variable(s) *may* not be secure!
    ================================================================================

    It looks as though your script has no bugs (at least, on the surface),
    so here is the output you have been waiting for:
    ================================================================================
    Here are the results of your query: <BR><HR>
    Login name: shishir                     In real life: Shishir Gundavaram
    Directory: /home/shishir                Shell: /usr/local/bin/tcsh
    On since Oct 26 23:11:27 on ttyp0 from nmrc.bu.edu
    Mail last read Mon Oct 27 00:03:54 1995
    No Plan.
    <HR>
    ================================================================================

It will display the output generated by the CGI program. It also outputs various other information, including possible security holes. Here is a list of the exact informational messages that CGI Lint outputs:

- The configuration file (that holds the environment variable data) could not be found. This file is needed to run this program. Please check and try again.

- The NCSA server resource map configuration file (srm.conf) could not be found. This might be due to the way your server is set up. In order to rectify the situation, define a variable called SERVER_ROOT (with the correct server root directory) in the configuration file, and try again.

- Sorry, either the file extension or the path to your CGI script is not valid. Check both of these to make sure they are configured in the NCSA server resource map configuration (srm.conf) file.

- You do not have the necessary privileges to run the specified script. Use the chmod command to change the permissions, and try again.

- The CGI program that is specified in the configuration file does not exist. Please check the path, and try again.

- The CGI program that is specified could not be opened. Please check the permissions and try again.

- The interpreter you specified either does not exist, is not readable, or is not a binary file. Please check the path, and try again.

- The script you specified does not have a header line that points to a interpreter that will execute the script. The header line should be something like this:

  #!/usr/local/bin/perl

- Oops! The script you wrote had errors. I will list all the bugs here. Please fix them and try again. Here they are:

- While looking at your Perl script for possible security holes and "open" commands, I came across the following *errors*:

- While looking at your Perl script for possible security holes and "open" commands, I came across the following statements that *might* constitute a security breach:

- The data file (that holds the potential form data) could not be found. Please check the file specification and try again.

- A data file to store the simulated POST data cannot be created. Please check to see if you have privileges to write to the /tmp directory.

- One of the filenames that you listed in the simulated multipart data file does not exist. Be sure to check all possible fields, and try again.

- The CONTENT_TYPE variable in your data file is not set correctly. You do not have to set a value for this, as I will default it to:

  application/x-www-form-urlencoded

  But, if you do set a value for this variable, it has to be either the one mentioned above, or:

  multipart/form-data

  If you specify an encoding type of multipart/form-data in the configuration file, I will create a random boundary, and set the CONTENT_TYPE to the following:

  multipart/form-data; boundary=--------------Some Random Boundary

- The REQUEST_METHOD variable in your data file is not set correctly. It has to have a value of either GET or POST.

- Your NPH (Non-Parsed-Header) script does not output the correct HTTP response. The first line has to be something like:

  HTTP/1.0 200 OK

- A serious error! Either you are not outputting a **BLANK** line after the HTTP headers, *OR* you are trying to send invalid (or undefined) HTTP headers. Please check the output of your script and try again.

- It looks as though your script has no bugs (at least, on the surface), so here is the output you have been waiting for:

- The *system* command was detected in your script. Make sure to turn output buffering off by adding the following line to your script:

  $| = 1;

Logging and Simulation |  | Set UID/GID Wrapper