<commit_before># Clammit

[![Build Status](https://travis-ci.org/ifad/clammit.svg)](https://travis-ci.org/ifad/clammit)
[![Code Climate](https://codeclimate.com/github/ifad/clammit/badges/gpa.svg)](https://codeclimate.com/github/ifad/clammit)

Clammit is a proxy that will perform virus scans of files uploaded via http requests,
including `multipart/form-data`.  If a virus exists, it will reject the request out of
hand. If no virus exists, the request is then forwarded to the application and
it's response returned in the upstream direction.

As the name implies, Clammit offloads the virus detection to the ClamAV virus
detection server (clamd).

## Usage

Clammit is intended to be used as an internal proxy, a sort of middleware. It
is best to only pass requests that include a file upload, however requests that
aren't `POST`/`PUT`/`PATCH` are passed through directly without being scanned.

As an example, say you have a `foo` Rails application that is configured in Nginx
like this:

```nginx
server {
  listen 80;
  server_name foobar.example.com;

  root /home/foobar/public;
  try_files $uri/index.html $uri @foobar;

  location @foobar {
    access_log /var/log/nginx/foobar.app-access.log;
    error_log  /var/log/nginx/foobar.app-error.log

    proxy_set_header Host $http_host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_pass http://unix:/home/foobar/.unicorn.sock;
  }
}
```

Assuming you receive document uploads at `POST /documents`, to check them with
a Clammit that has been configured to listen on an UNIX socket in
`/var/run/clammit.sock`, you should add a location block like this:

```nginx
  location /documents {
    access_log /var/log/nginx/foobar.clammit-access.log;
    error_log  /var/log/nginx/foobar.clammit-error.log;

    proxy_set_header Host $http_host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

    # This is where clammit will forward requests that do not contain viruses
    # or that did not contain any data to scan.
    proxy_set_header X-Clammit-Backend unix:/home/foobar/.unicorn.sock;

    proxy_pass http://unix:/var/run/clammit.sock;
  }
```

All requests to `/documents` will then pass through Clammit, and uploads will
be scanned for viruses. To your application the request will just appear as
coming from Nginx, all cookies and the headers will be kept intact.

If a virus is detected, Clammit will reject the request (with a `418` status
code), and not forward it to your application. If you use an AJAX uploader, you
can interpret this response and show a nice error message to end users. Or you
could set a custom error page in Nginx.

## Configuration

You will need to create and edit a configuration file. An example is found in etc.sample/

The configuration is pretty simple:

```ini
[ application ]
listen          = :8438
application-url = http://host:port/path
clamd-url       = http://host:port/<commit_msg>doc: describe usage as a proxy and as a service<commit_after># Clammit

[![Build Status](https://travis-ci.org/ifad/clammit.svg)](https://travis-ci.org/ifad/clammit)
[![Code Climate](https://codeclimate.com/github/ifad/clammit/badges/gpa.svg)](https://codeclimate.com/github/ifad/clammit)

Clammit is a proxy that will perform virus scans of files uploaded via http requests,
including `multipart/form-data`.  If a virus exists, it will reject the request out of
hand. If no virus exists, the request is then forwarded to the application and
it's response returned in the upstream direction.

As the name implies, Clammit offloads the virus detection to the ClamAV virus
detection server (clamd).

## Usage

Clammit parses and processes inbound HTTP requests. When it handles a request whose
Content-Length is non-zero, it will attempt to decode multipart file uploads and pass
each part or the whole body to ClamAV. If ClamAV detects a virus, clammit will then
return a response with code 418 to the caller. Otherwise, it will continue processing.

Clammit can be be used in two ways: as an intercepting proxy or as a virus check service.

### Usage as a proxy

When used as a proxy, clammit sits in between the client and your application, thus
preventing uploads with virus files to reach your application, by returning a 418 to
your app's client. For this mode to work, clammit must be able to contact your app
directly, so it is best suited when clammit is executing on the same machine as your
app.

As an example, say you have a `foo` Rails application that is configured in Nginx
like this:

```nginx
server {
  listen 80;
  server_name foobar.example.com;

  root /home/foobar/public;
  try_files $uri/index.html $uri @foobar;

  location @foobar {
    access_log /var/log/nginx/foobar.app-access.log;
    error_log  /var/log/nginx/foobar.app-error.log;

    proxy_set_header Host $http_host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_pass http://unix:/home/foobar/.unicorn.sock;
  }
}
```

Assuming you receive document uploads at `POST /documents`, to check them with
a Clammit that has been configured to listen on an UNIX socket in
`/var/run/clammit.sock`, you should add a location block like this:

```nginx
  location /documents {
    access_log /var/log/nginx/foobar.clammit-access.log;
    error_log  /var/log/nginx/foobar.clammit-error.log;

    proxy_set_header Host $http_host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

    # This is where clammit will forward requests that do not contain viruses
    # or that did not contain any data to scan.
    proxy_set_header X-Clammit-Backend unix:/home/foobar/.unicorn.sock;

    proxy_pass http://unix:/var/run/clammit.sock;
  }
```

All requests to `/documents` will then pass through Clammit, and uploads will
be scanned for viruses. To your application the request will just appear as
coming from Nginx, all cookies and the headers will be kept intact.

If a virus is detected, Clammit will reject the request (with a `418` status
code), and not forward it to your application. If you use an AJAX uploader, you
can interpret this response and show a nice error message to end users. Or you
could set a custom error page in Nginx.

### Usage as a service

When used as a service, clammit can be anywhere in your architecture, and it will
return a 200 OK if the request has no virus, or a configurbale status code if a
virus is detected.

To scan a file, send it via HTTP - using any method you prefer - to the /clammit/scan endpoint.

Example, with cURL:

```sh
curl -sf http://localhost:8438/clammit/scan -d /some/file
```

Or with Python:

```py
import requests

>>> r = requests.post('http://localhost:8438/clammit/scan', files={'file': open('/etc/passwd', 'rb')})
>>> r.status_code
200

>>> r = requests.post('http://localhost:8438/clammit/scan', files={'file': b'X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*'})
>>> r.status_code
418
```

Or with Ruby:

```ruby
require 'httparty'

>> r = HTTParty.post('http://localhost:8438/clammit/scan', body: { file: File.open('/etc/passwd') })
>> r.code
=> 200

>> r = HTTParty.post('http://localhost:8438/clammit/scan', multipart: true, body: { file: 'X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*' })
>> r.code
=> 418
```

## Configuration

You will need to create and edit a configuration file. An example is found in etc.sample/

The configuration is pretty simple:

```ini
[ application ]
listen          = :8438
application-url = http://host:port/path
clamd-url       = http://host:port/
{'fraction_non_alphanumeric': 0.0958067940552017, 'fraction_numerical': 0.00942144373673036, 'mean_word_length': 4.041471571906355, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 25, 'https://': 8, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '274822', 'n_tokens_mistral': 2488, 'n_tokens_neox': 2352, 'n_words': 906}