---
name: "haserl"
section: "1"
platform: "linux"
package: "haserl"
version: "0.9.36-2.1"
release: "Debian 13 (Trixie)"
platforms: ["linux"]
url: "https://new.carta.tech/haserl"
---

# haserl(1)

A cgi scripting program for embedded environments

## NAME

haserl - A cgi scripting program for embedded environments

## SYNOPSIS

**#!/usr/bin/haserl \[--shell=***pathspec***\] \[--upload-dir=***dirspec***\] \[--upload-handler=***handler***\] \[--upload-limit=***limit***\] \[--accept-all\] \[--accept-none\] \[--silent\] \[--debug\]**

\[ text \] \[ \<% shell script %\> \] \[ text \] ...

## DESCRIPTION

Haserl is a small cgi wrapper that allows "PHP" style cgi programming,  but uses a UNIX bash-like shell or Lua  as the programming language. It is  very small, so it can be used in embedded environments, or where  something like PHP is too big.

It combines three features into a small cgi engine:

-

  It parses POST and GET requests, placing form-elements as  name=value  pairs into the environment for the CGI script to use.  This is somewhat like  the  *uncgi* wrapper.

-

  It opens a shell, and translates all text into printable statements. All text within \<% ... %\> constructs are passed verbatim to the shell. This is somewhat like writing *PHP* scripts.

-

  It can optionally be installed to drop its permissions to the owner of the  script, giving it some of the security features of *suexec* or *cgiwrapper*.

## OPTIONS SUMMARY

This is a summary of the command-line options.  Please see the  **OPTIONS** section under the long option name for a complete description.

\-a  --accept-all\
 \-n  --accept-none\
 \-d  --debug \
 \-s, --shell\
 \-S, --silent\
 \-U, --upload-dir\
 \-u, --upload-limit\
 \-H, --upload-handler\

## OPTIONS

- **--accept-all**

  The program normally accepts POST data only when the REQUEST\_METHOD is POST and only accepts data on the URL  data when the REQUEST\_METHOD is GET.   This option allows both POST and URL data to be accepted regardless of the REQUEST\_METHOD.  When this option is set,  the REQUEST\_METHOD takes precedence (e.g.  if the method is POST, FORM\_variables are taken  from COOKIE data, GET data, and POST data, in that order.   If the method is GET, FORM\_variables are taken from COOKIE data, POST data, and GET data.)  The default is not to accept all input methods - just the COOKIE data and the REQUEST\_METHOD.

- **--accept-none**

  If given, haserl will not parse standard input as http content before  processing the script.  This is useful if calling a haserl script from  another haserl script.

- **--debug**

  Instead of executing the script, print out the script that would be executed.  If the environment variable 'REQUEST\_METHOD' is set, the data is sent with the plain/text content type.  Otherwise, the shell script is printed verbatim.

- **--shell=***pathspec*

  Specify an alternative bash-like shell to use. Defaults to "/bin/sh"

  To include shell parameters do not use the --shell=/bin/sh format. Instead, use the alternative format without the "=", as in --shell "/bin/bash --norc". Be sure to quote the option string to protect any special characters.

  If compiled with Lua libraries, then the string "lua" is used to use an integrated Lua vm.  This string is case sensitive.  Example: **--shell=***lua*

  An alternative is "luac".  This causes the haserl and lua parsers to be disabled, and the  script is assumed to be a precompiled lua chunk.  See  **LUAC** below for more information.

- **--silent**

  Haserl normally prints an informational message on error conditions.  This  suppresses the error message, so that the use of haserl is not advertised.

- **--upload-dir=***dirspec*

  Defaults to "/tmp". All uploaded files are created with temporary filename in this directory   **HASERL\_xxx\_path** contains the name of the temporary file. **FORM\_xxx\_name** contains the original name of the file, as specified by the client.

- **--upload-handler=***pathspec*

  When specified, file uploads are handled by this handler, rather than written to temporary files.  The full pathspec must be given (the PATH is not  searched), and the upload-handler is given one command-line parameter:   The name of the FIFO on which the upload file will be sent.  In addition, the handler may receive 3 environment variables: **CONTENT\_TYPE**, **FILENAME**, and **NAME**. These reflect the MIME content-disposition headers for the content. Haserl will fork the handler for each file uploaded, and will send the contents  of the upload file to the specified FIFO.  Haserl will then block until  the handler terminates.  This method is for experts only.

- **--upload-limit=***limit*

  Allow a mime-encoded file up to  *limit KB* to be uploaded.  The default is  *0KB* (no uploads allowed).    Note that mime-encoding adds 33% to the size of the data.

## OVERVIEW OF OPERATION

In general, the web server sets up several environment variables, and then uses  *fork* or another method to run the CGI script.  If the script uses the  *haserl* interpreter, the following happens:

-

  If  *haserl* is installed suid root, then uid/gid is set to the owner of the script.

  The environment is scanned for  *HTTP\_COOKIE*, which may have been set by the web server.   If it exists, the parsed contents are placed in the local environment.

  The environment is scanned for  *REQUEST\_METHOD*, which was set by the web server.  Based on the request method, standard input  is read and parsed.  The parsed contents are placed in the local environment.

  The script is tokenized, parsing  *haserl* code blocks from raw text.  Raw text is converted into "echo" statements, and  then all tokens are sent to the sub-shell.

  *haserl* forks and a sub-shell (typically */bin/sh*) is started.

  All tokens are sent to the STDIN of the sub-shell, with a trailing  **exit** command.

  When the sub-shell terminates, the  *haserl* interpreter performs final cleanup and then terminates.

## CLIENT SIDE INPUT

The  *haserl* interpreter will decode data sent via the HTTP\_COOKIE environment variable, and the GET or POST method from the client, and store them as environment variables that can be accessed by haserl.   The name of the variable follows the name given in the source, except that a prefix ( *FORM\_*) is prepended.  For example, if the client sends "foo=bar", the environment variable is **FORM\_foo**=**bar**.

For the HTTP\_COOKIE method, variables are also stored with the prefix ( *COOKIE\_*) added.  For example, if HTTP\_COOKIE includes "foo=bar", the environment variable is **COOKIE\_foo**=**bar**.

For the GET method, data sent in the form %xx is translated into the characters they represent, and variables are also stored with the prefix ( *GET\_*) added.  For example, if QUERY\_STRING includes "foo=bar", the environment variable is **GET\_foo**=**bar**.

For the POST method, variables are also stored with the prefix ( *POST\_*) added.  For example, if the post stream includes "foo=bar", the environment variable is **POST\_foo**=**bar**.

Also, for the POST method, if the data is sent using  *multipart/form-data* encoding, the data is automatically decoded.   This is typically used when  files are uploaded from a web client using \<input type=file\>.

- **NOTE**

  When a file is uploaded to the web server, it is stored in the  *upload-dir* directory.  **FORM\_variable\_name=** contains the name of the file uploaded (as specified by the client.)  **HASERL\_variable\_path=** contains the name of the file in  *upload-dir* that holds the uploaded content.   To prevent malicious clients from  filling up  *upload-dir* on your web server, file uploads are only allowed when the *--upload-limit* option is used to specify how large a file can be uploaded.   Haserl automatically deletes the temporary file when the script is finished.  To keep the file, move it or rename it somewhere in the script.

-

  Note that the filename is stored in  **HASERL\_***variable\_path* This is because the FORM\_, GET\_, and POST\_ variables are modifiable by the client,  and a malicious client can set a second variable with the name  *variable\_path=/etc/passwd*. Earlier versions did not store the pathspec in  **HASERL** namespace.   *To maintain backward compailibility, the name of the temporary file* *is also stored in* **FORM\_variable=** *and*   **POST\_variable=.** *This is considered unsafe and should not be used.*

If the client sends data  *both* by POST and GET methods, then  *haserl* will parse only the data that corresponds with the  *REQUEST\_METHOD* variable set by the web server, unless the  *accept-all* option has been set.   For example, a form called via POST method, but having a  URI of some.cgi?foo=bar&otherdata=something will have the POST data parsed, and the  *foo* and *otherdata* variables are ignored.

If the web server defines a  *HTTP\_COOKIE* environment variable, the cookie data is parsed.  Cookie data is parsed  *before* the GET or POST data, so in the event of two variables of the same name, the  GET or POST data overwrites the cookie information.

When multiple instances of the same variable are sent from different sources, the FORM\_variable will be set according to the order in which variables are processed.  HTTP\_COOKIE is always processed first, followed by the REQUEST\_METHOD.  If the accept-all option has been set, then HTTP\_COOKIE is processed first, followed by the method not specified by REQUEST\_METHOD, followed by the REQUEST\_METHOD.  The last instance of the variable will be used to set FORM\_variable.  Note that the variables are also separately creates as COOKIE\_variable, GET\_variable and POST\_variable.  This allows the use of overlapping names from each source.

When multiple instances of the same variable are sent from the same source,  only the last one is saved.  To keep all copies (for multi-selects, for  instance), add "\[\]" to the end of the  variable name.  All results will be returned, separated by newlines.   For example, host=Enoch&host=Esther&host=Joshua results in "FORM\_host=Joshua".  host\[\]=Enoch&host\[\]Esther&host\[\]=Joshua results in "FORM\_host=Enoch\\nEsther\\nJoshua"

## LANGUAGE

The following language structures are recognized by  *haserl*.

- **RUN**

  ```
  <% [shell script] %>

  ```

  Anything enclosed by \<% %\> tags is sent to the sub-shell for execution.   The  text is sent verbatim.

- **INCLUDE**

  ```
  <%in pathspec %>

  ```

  Include another file verbatim in this script.  The file is included when the script is initially parsed.

- **EVAL**

  ```
  <%= expression %>

  ```

  print the shell expression.  Syntactic sugar for "echo expr".

- **COMMENT**

  ```
  <%# comment %>

  ```

  Comment block.  Anything in a comment block is not parsed.  Comments can be nested and can contain  other haserl elements.

## EXAMPLES

- **WARNING**

  The examples below are simplified to show how to use  *haserl*. You should be familiar with basic web scripting security before using  *haserl* (or any scripting language) in a production environment.

- **Simple Command**

  ```
  #!/usr/local/bin/haserl
  content-type: text/plain

  <%# This is a sample "env" script %>
  <% env %>
  ```

  Prints the results of the *env* command as a mime-type "text/plain" document. This is the  *haserl* version of the common  *printenv* cgi.

- **Looping with dynamic output**

  ```
  #!/usr/local/bin/haserl
  Content-type: text/html

  <html>
  <body>
  <table border=1><tr>
  <% for a in Red Blue Yellow Cyan; do %>
  	<td bgcolor="<% echo -n "$a" %>"><% echo -n "$a" %></td>
  	<% done %>
  </tr></table>
  </body>
  </html>
  ```

  Sends a mime-type "text/html" document to the client, with an html table of with elements labeled with the background color.

- **Use Shell defined functions.**

  ```
  #!/usr/local/bin/haserl
  content-type: text/html

  <% # define a user function
     table_element() {
         echo "<td bgcolor=\"$1\">$1</td>"
      }
     %>
  <html>
  <body>
  <table border=1><tr>
  <% for a in Red Blue Yellow Cyan; do %>
  	<% table_element $a %>
   	<% done %>
  </tr></table>
  </body>
  </html>
  ```

  Same as above, but uses a shell function instead of embedded html.

- **Self Referencing CGI with a form**

  ```
  #!/usr/local/bin/haserl
  content-type: text/html

  <html><body>
  <h1>Sample Form</h1>
  <form action="<% echo -n $SCRIPT_NAME %>" method="GET">
  <% # Do some basic validation of FORM_textfield
     # To prevent common web attacks
     FORM_textfield=$( echo "$FORM_textfield" | sed "s/[^A-Za-z0-9 ]//g" )
     %>
  <input type=text name=textfield
  	Value="<% echo -n "$FORM_textfield" | tr a-z A-Z %>" cols=20>
  <input type=submit value=GO>
  </form></html>
  </body>
  ```

  Prints a form.  If the client enters text in the form, the CGI is reloaded (defined by  *$SCRIPT\_NAME*) and the textfield is sanitized to prevent web attacks, then the form is redisplayed with the text the user entered.  The text is uppercased.

- **Uploading a File**

  ```
  #!/usr/local/bin/haserl --upload-limit=4096 --upload-dir=/tmp
  content-type: text/html

  <html><body>
  <form action="<% echo -n $SCRIPT_NAME %>" method=POST enctype="multipart/form-data" >
  <input type=file name=uploadfile>
  <input type=submit value=GO>
  <br>
  <% if test -n "$HASERL_uploadfile_path"; then %>
          <p>
          You uploaded a file named <b><% echo -n $FORM_uploadfile_name %></b>, and it was
          temporarily stored on the server as <i><% echo $HASERL_uploadfile_path %></i>.  The
          file was <% cat $HASERL_uploadfile_path | wc -c %> bytes long.</p>
          <% rm -f $HASERL_uploadfile_path %><p>Don't worry, the file has just been deleted
          from the web server.</p>
  <% else %>
          You haven't uploaded a file yet.
  <% fi %>
  </form>
  </body></html>
  ```

  Displays a form that allows for file uploading.  This is accomplished by using the  **--upload-limit** and by setting the form  *enctype* to *multipart/form-data.* If the client sends a file, then some information regarding the file is printed, and then deleted.  Otherwise, the form states that the client has not uploaded a file.

- **RFC-2616 Conformance**

  ```
  #!/usr/local/bin/haserl
  <% echo -en "content-type: text/html\r\n\r\n" %>
  <html><body>
    ...
  </body></html>
  ```

  To fully comply with the HTTP specification, headers should be terminated using CR+LF, rather than the normal unix LF line termination only.  The above syntax can be used to produce RFC 2616 compliant headers.

## ENVIRONMENT

In addition to the environment variables inherited from the web server, the following environment variables are always defined at startup:

- HASERLVER

  *haserl* version - an informational tag.

- SESSIONID

  A hexadecimal tag that is unique for the life of the CGI (it is generated when the cgi starts; and does not change until another POST or GET query is generated.)

- HASERL\_ACCEPT\_ALL

  If the *--accept-all* flag was set, *-1*, otherwise *0*.

- HASERL\_SHELL

  The name of the shell haserl started to run sub-shell commands in.

- HASERL\_UPLOAD\_DIR

  The directory haserl will use to store uploaded files.

- HASERL\_UPLOAD\_LIMIT

  The number of KB that are allowed to be sent from the client to the server.

These variables can be modified or overwritten within the script, although the ones starting with "HASERL\_" are informational only, and do not affect the running script.

## SAFETY FEATURES

There is much literature regarding the dangers of using shell to program CGI scripts. *haserl* contains *some* protections to mitigate this risk.

- **Environment Variables**

  The code to populate the environment variables is outside the scope of the sub-shell.   It parses on the characters ? and  &, so it is harder for a client to do "injection" attacks.  As an example,  *foo.cgi?a=test;cat /etc/passwd* could result in a variable being assigned the value  **test** and then the results of running  *cat /etc/passwd* being sent to the client.   *Haserl* will assign the variable the complete value: **test;cat /etc/passwd**

  It is safe to use this "dangerous" variable in shell scripts by enclosing it in quotes; although validation should be done on all input fields.

- **Privilege Dropping**

  If installed as a suid script,  *haserl* will set its uid/gid to that of the owner of the script.  This can be used to have a set of CGI scripts that have various privilege.  If the  *haserl* binary is not installed suid, then the CGI scripts will run with the uid/gid of the web server.

- **Reject command line parameters given on the URL**

  If the URL does not contain an unencoded "=", then the CGI spec states the options are to be used as command-line parameters to the program.  For instance, according to the CGI spec: *http://192.168.0.1/test.cgi?--upload-limit%3d2000&foo%3dbar*

  Should set the upload-limit to 2000KB in addition to setting "Foo=bar".  To protect against clients enabling their own uploads, *haserl* rejects any command-line options beyond argv\[2\].   If invoked as a #!  script, the interpreter is argv\[0\], all command-line options listed in the #! line are  combined into argv\[1\], and the script name is argv\[2\].

## LUA

If compiled with lua support,  **--shell=lua** will enable lua as the script language instead of bash shell.  The environment variables  (SCRIPT\_NAME, SERVER\_NAME, etc) are placed in the ENV table, and the form variables are  placed in the FORM table.  For example, the self-referencing form above can be written like this:

```
#!/usr/local/bin/haserl --shell=lua
content-type: text/html

<html><body>
<h1>Sample Form</h1>
<form action="<% io.write(ENV["SCRIPT_NAME"]) %>" method="GET">
<% # Do some basic validation of FORM_textfield
   # To prevent common web attacks
   FORM.textfield=string.gsub(FORM.textfield, "[^%a%d]", "")
   %>
<input type=text name=textfield
	Value="<% io.write (string.upper(FORM.textfield)) %>" cols=20>
<input type=submit value=GO>
</form></html>
</body>
```

The \<%= operator is syntactic sugar for  *io.write (tostring( ... ))* So, for example, the Value= line above could be written: **Value="\<%= string.upper(FORM.textfield) %\>" cols=20\>**

haserl lua scripts can use the function **haserl.loadfile(***filename***)** to process a target script as a haserl (lua) script.  The function returns a type of "function".

For example,

bar.lsp

```
<% io.write ("Hello World" ) %>

Your message is <%= gvar %>

-- End of Include file --
```

foo.haserl

```
#!/usr/local/bin/haserl --shell=lua
<% m = haserl.loadfile("bar.lsp")
   gvar = "Run as m()"
   m()
   gvar = "Load and run in one step"
   haserl.loadfile("bar.lsp")()
%>
```

Running  *foo* will produce:

```
Hello World
Your message is Run as m()
-- End of Include file --
Hello World
Your message is Load and run in one step
-- End of Include file --
```

This function makes it possible to have nested haserl server pages - page snippets that are  processed by the haserl tokenizer.

## LUAC

The *luac* "shell" is a precompiled lua chunk, so interactive editing and testing of scripts is  not possible. However, haserl can be compiled with luac support only, and this allows  lua support even in a small memory environment.  All haserl lua features listed above  are still available.  (If luac is the only shell built into haserl, the haserl.loadfile is disabled, as the haserl parser is not compiled in.)

Here is an example of a trivial script, converted into a luac cgi script:

Given the file test.lua:

```
print ("Content-Type: text/plain0n")
print ("Your UUID for this run is: " .. ENV.SESSIONID)
```

It can be compiled with luac:

luac -o test.luac -s test.lua

And then the haserl header added to it:

echo '#!/usr/bin/haserl --shell=luac' \| cat - test.luac  \>luac.cgi

Alternatively, it is possible to develop an entire website using the standard lua shell, and then have haserl itself preprocess the scripts for the luac compiler as part of a build process.  To do this, use --shell=lua, and develop the website.  When ready to build the runtime environment, add the --debug line to your lua scripts, and run them outputting the results to .lua source files.  For example:

Given the haserl script test.cgi:

```
#!/usr/bin/haserl --shell=lua --debug
Content-Type: text/plain
Your UUID for this run is <%= ENV.SESSIONID %>
```

Precompile, compile, and add the haserl luac header:

```
./test.cgi > test.lua
luac -s -o test.luac test.lua
echo '#!/usr/bin/haserl --shell=luac' | cat - test.luac >luac.cgi
```

## BUGS

Old versions of haserl used \<? ?\> as token markers, instead of \<% %\>.  Haserl will fall back to using \<? ?\>  *if \<% does not appear anywhere in the script.*

When files are uploaded using RFC-2388, a temporary file is created.  The name of the file is stored in  **FORM\_variable\_name**, **POST\_variable\_name**, and **HASERL\_variable\_name**. Only **HASERL\_variable\_name** should be used - the others can be overwritten by a malicious client.

## NAME

The name "haserl" comes from the Bavarian word for "bunny." At first glance it may be small and cute, but *haserl* is more like the bunny from  *Monty Python & The Holy Grail*. In the words of Tim the Wizard,  *That's the most foul, cruel & bad-tempered rodent you ever set eyes on!*

Haserl can be thought of the cgi equivalent to  *netcat*. Both are small, powerful, and have very little in the way of extra features.  Like  *netcat*, *haserl* attempts to do its job with the least amount of extra "fluff".

## AUTHOR

Nathan Angelacos \<nangel@users.sourceforge.net\>

## SEE ALSO

**php**(http://www.php.net) **uncgi**(http://www.midwinter.com/~koreth/uncgi.html) **cgiwrapper**(http://cgiwrapper.sourceforge.net)
