Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 4 additions & 20 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,27 +23,11 @@ jobs:
- name: Checkout
uses: actions/checkout@v4

- name: Setup mdBook
uses: peaceiris/actions-mdbook@v2
with:
mdbook-version: 'latest'

- name: Install mdbook-mermaid
run: |
VERSION=$(curl -s https://api.github.com/repos/badboy/mdbook-mermaid/releases/latest | grep tag_name | cut -d '"' -f 4)
curl -sSL "https://github.com/badboy/mdbook-mermaid/releases/download/${VERSION}/mdbook-mermaid-${VERSION}-x86_64-unknown-linux-gnu.tar.gz" | tar -xz
sudo mv mdbook-mermaid /usr/local/bin/
mdbook-mermaid install .

- name: Install mdbook-linkcheck
run: |
curl -sSL https://github.com/Michael-F-Bryan/mdbook-linkcheck/releases/latest/download/mdbook-linkcheck.x86_64-unknown-linux-gnu.zip -o linkcheck.zip
unzip -q -o linkcheck.zip
chmod +x mdbook-linkcheck
sudo mv mdbook-linkcheck /usr/local/bin/

- name: Setup Rust toolchain
uses: dtolnay/rust-toolchain@stable

- name: Build documentation
run: mdbook build
run: ./scripts/mdbook.sh build

- name: Upload artifact
uses: actions/upload-pages-artifact@v3
Expand Down
2 changes: 1 addition & 1 deletion book.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ site-url = "https://coder.github.io/httpjail/"
cname = ""
mathjax-support = false
copy-fonts = true
additional-css = []
additional-css = ["docs/custom.css"]
additional-js = ["mermaid.min.js", "mermaid-init.js"]
no-section-label = false
fold.enable = true
Expand Down
1 change: 1 addition & 0 deletions docs/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

# Advanced

- [Request Body Limiting](./advanced/request-body-limiting.md)
- [TLS Interception](./advanced/tls-interception.md)
- [DNS Exfiltration](./advanced/dns-exfiltration.md)
- [Server Mode](./advanced/server-mode.md)
Expand Down
148 changes: 148 additions & 0 deletions docs/advanced/request-body-limiting.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
# Request Body Limiting

The `max_tx_bytes` feature allows you to limit the total size of HTTP requests sent to upstream servers.

This is primarily designed for mitigating code exfiltration attacks through covert channels.

## Size Calculation

The `max_tx_bytes` limit applies to **complete** HTTP requests, including:

1. **Request line**: `METHOD /path HTTP/1.1\r\n`
2. **Headers**: Each header as `Name: Value\r\n`
3. **Header separator**: Final `\r\n` between headers and body
4. **Body**: Request body bytes

## Response Format

To enable request body limiting, return an object with `max_tx_bytes` in your rule response:

```javascript
// JavaScript engine
{allow: {max_tx_bytes: 1024}} // Limit to 1KB total request size
```

```json
// Line processor engine
{"allow": {"max_tx_bytes": 1024}}
```

> **Note**: The `max_tx_bytes` feature is only available in the JavaScript (`--js`) and Line Processor (`--proc`) engines, not in Shell scripts.

## Behavior

The limiting behavior depends on whether the request includes a `Content-Length` header:

### With Content-Length Header

When the request includes a `Content-Length` header (most standard HTTP clients):

1. **Early Detection**: httpjail calculates the total request size
2. **Immediate Rejection**: If it exceeds `max_tx_bytes`, the client receives a `413 Payload Too Large` error immediately
3. **No Upstream Contact**: The upstream server is never contacted, preventing unnecessary load
4. **Clear Feedback**: The error message indicates the actual size and limit

**Example error response:**
```
HTTP/1.1 413 Payload Too Large
Content-Type: text/plain

Request body size (5000 bytes) exceeds maximum allowed (1024 bytes)
```

### Without Content-Length Header

When the request uses chunked encoding or doesn't include `Content-Length`:

1. **Stream Truncation**: The request body is truncated at the limit during streaming
2. **Upstream Receives Partial**: The upstream server receives exactly `max_tx_bytes` total bytes (url + headers + truncated body)
3. **Connection Closes**: The connection terminates after reaching the limit

## Examples

### JavaScript Engine - Upload Endpoint Limiting

```javascript
// Limit upload endpoints to 1KB total request size
const uploadHosts = ['uploads.example.com', 'upload.github.com'];

uploadHosts.includes(r.host)
? {allow: {max_tx_bytes: 1024}}
: r.host.endsWith('.example.com')
```

### Line Processor Engine - Python Example

```python
#!/usr/bin/env python3
import sys, json

upload_hosts = {'uploads.example.com', 'data.api.com'}

for line in sys.stdin:
try:
req = json.loads(line)
if req['host'] in upload_hosts:
# Limit upload endpoints to 1KB requests
# Returns 413 error if Content-Length exceeds limit
# Truncates body if no Content-Length header
response = {"allow": {"max_tx_bytes": 1024}}
print(json.dumps(response))
elif req['host'].endswith('.example.com'):
print("true")
else:
print("false")
except:
print("false")
sys.stdout.flush()
```


## Use Cases

### 1. Limiting File Uploads

Prevent users from uploading large files to specific endpoints:

```javascript
// JavaScript engine
const uploadPaths = ['/upload', '/api/files'];
uploadPaths.some(path => r.path.startsWith(path))
? {allow: {max_tx_bytes: 10485760}} // 10MB limit
: true
```

### 2. API Cost Control

Limit request sizes to metered APIs to prevent unexpected costs:

```javascript
// JavaScript engine
r.host === 'api.expensive-service.com'
? {allow: {max_tx_bytes: 1024}} // 1KB limit for expensive API
: true
```

### 3. Data Exfiltration Prevention

Prevent large data uploads that might indicate data exfiltration:

```javascript
// JavaScript engine
const externalHosts = ['pastebin.com', 'transfer.sh', 'file.io'];
externalHosts.some(host => r.host.includes(host))
? {allow: {max_tx_bytes: 4096}} // 4KB limit for paste sites
: true
```

## Limitations

- **Shell scripts**: The `max_tx_bytes` feature is not available when using shell script rules (`--shell`)
- **HTTP wire format**: The byte count is based on HTTP wire format, not just the body size
- **Partial uploads**: When truncating (no Content-Length), the upstream server receives incomplete data which may cause application errors

## See Also

- [JavaScript Engine](../guide/rule-engines/javascript.md)
- [Line Processor Engine](../guide/rule-engines/line-processor.md)
- [Configuration](../guide/configuration.md)
22 changes: 22 additions & 0 deletions docs/custom.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/* Minimal CSS to enable multi-line code in tables */
/* Let mdbook theme handle all other styling */

table td pre {
margin: 0;
}

table td pre code {
display: block;
}

/* Make <br> tags work as line breaks in code */
table td pre code br {
display: block;
content: "";
}

/* Keep tables left-aligned - override general.css margin: 0 auto */
.table-wrapper table {
margin-left: 0;
margin-right: 0;
}
11 changes: 6 additions & 5 deletions docs/guide/rule-engines/javascript.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,11 @@ allowedHosts.includes(r.host);
httpjail --js-file rules.js -- command
```

## Response Types
## Response Format

Your JavaScript can return:
{{#include ../../includes/response-format-table.md}}

- **Boolean**: `true` to allow, `false` to deny
- **Object with message**: `{allow: false, deny_message: "Custom error"}`
- **Just a message**: `{deny_message: "Blocked"}` (implies deny)
**Examples:**

```javascript
// Simple boolean
Expand All @@ -59,6 +57,9 @@ false // Deny

// Conditional with message
r.host === 'facebook.com' ? {deny_message: 'Social media blocked'} : true

// Limit request upload size to 1KB (headers + body)
({allow: {max_tx_bytes: 1024}})
```

## Common Patterns
Expand Down
14 changes: 10 additions & 4 deletions docs/guide/rule-engines/line-processor.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,12 @@ Each request is sent as a single JSON line:

### Response Format

Your processor must respond with one line per request:
Your processor must respond with one line per request.

- **Boolean strings**: `"true"` (allow) or `"false"` (deny)
- **JSON object**: `{"allow": false, "deny_message": "Blocked by policy"}`
- **JSON with message only**: `{"deny_message": "Blocked"}` (implies deny)
{{#include ../../includes/response-format-table.md}}

**Additional:**
- **Boolean strings**: `"true"` (allow) or `"false"` (deny) - same as boolean
- **Any other text**: Treated as deny with that text as the message (e.g., `"Access denied"` becomes a deny with message "Access denied")

## Command Line Usage
Expand All @@ -56,12 +57,17 @@ httpjail --proc "./filter.sh --strict" -- your-command
import sys, json

allowed_hosts = {'github.com', 'api.github.com'}
upload_hosts = {'uploads.example.com'}

for line in sys.stdin:
try:
req = json.loads(line)
if req['host'] in allowed_hosts:
print("true")
elif req['host'] in upload_hosts:
# Limit upload endpoints to 1KB requests
response = {"allow": {"max_tx_bytes": 1024}}
print(json.dumps(response))
else:
# Can return JSON for custom messages
response = {"allow": False, "deny_message": f"{req['host']} not allowed"}
Expand Down
9 changes: 9 additions & 0 deletions docs/includes/response-format-table.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
| Response Format | Meaning |
|----------------|---------|
| `true` | Allow the request |
| `false` | Deny the request |
| `{allow: true}` | Allow (object form) |
| `{allow: false}` | Deny (object form) |
| <pre><code>{<br> allow: false,<br> deny_message: "Access denied"<br>}</code></pre> | Deny with custom message |
| `{deny_message: "Blocked"}` | Deny (message implies deny) |
| <pre><code>{<br> allow: {<br> max_tx_bytes: 1024<br> }<br>}</code></pre> | Allow with [request body limiting](../../advanced/request-body-limiting.md) |
Loading