> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/nearai/ironclaw/llms.txt
> Use this file to discover all available pages before exploring further.

# Network Security

> HTTP endpoint allowlisting, rate limiting, and request validation for WASM tools

IronClaw implements **strict network controls** to ensure WASM tools can only access approved API endpoints with proper rate limiting and size constraints.

## Security Model

WASM tools have **zero network access** by default. HTTP capability must be:

1. **Explicitly granted** via capabilities file
2. **Allowlisted** to specific hosts and paths
3. **Rate limited** to prevent abuse
4. **Size constrained** for requests and responses
5. **HTTPS-only** (no plaintext HTTP)

## HTTP Request Flow

```
┌─────────────────────────────────────────────────────────────────┐
│                    HTTP Request Pipeline                        │
│                                                                  │
│   WASM Tool                                                      │
│      │                                                          │
│      ▼                                                          │
│   1. http_request(url, method, headers, body)                   │
│      │                                                          │
│      ▼                                                          │
│   2. Allowlist Validator                                         │
│      ├─► Host in allowlist?                                   │
│      ├─► Path prefix matches?                                 │
│      ├─► HTTP method allowed?                                 │
│      └─► HTTPS only?                                          │
│      │                                                          │
│      ▼                                                          │
│   3. Leak Detector (Outbound)                                    │
│      ├─► Scan URL for secrets                                 │
│      ├─► Scan headers for secrets                             │
│      └─► Scan body for secrets                                │
│      │                                                          │
│      ▼                                                          │
│   4. Rate Limiter                                                │
│      ├─► Check requests/minute                                │
│      └─► Check requests/hour                                  │
│      │                                                          │
│      ▼                                                          │
│   5. Size Validator                                              │
│      ├─► Request body < max_request_bytes?                    │
│      └─► Headers reasonable?                                  │
│      │                                                          │
│      ▼                                                          │
│   6. Credential Injector                                         │
│      └─► Inject secrets for matching hosts                   │
│      │                                                          │
│      ▼                                                          │
│   7. Execute HTTP Request (reqwest)                              │
│      │                                                          │
│      ▼                                                          │
│   8. Response Validator                                          │
│      └─► Response body < max_response_bytes?                  │
│      │                                                          │
│      ▼                                                          │
│   9. Leak Detector (Inbound)                                     │
│      └─► Scan response for secrets                            │
│      │                                                          │
│      ▼                                                          │
│   10. Return to WASM Tool                                        │
└─────────────────────────────────────────────────────────────────┘
```

## Endpoint Allowlisting

The first and most critical defense: WASM tools can **only** make HTTP requests to explicitly approved endpoints.

### Allowlist Configuration

Defined in `*.capabilities.json`:

```json theme={null}
{
  "http": {
    "allowlist": [
      {
        "host": "api.openai.com",
        "path_prefix": "/v1/",
        "methods": ["POST"]
      },
      {
        "host": "api.stripe.com",
        "path_prefix": "/v1/",
        "methods": ["GET", "POST"]
      },
      {
        "host": "*.example.com",
        "methods": ["GET"]
      }
    ]
  }
}
```

### EndpointPattern Structure

From [src/tools/wasm/capabilities.rs:172-200](~/workspace/source/src/tools/wasm/capabilities.rs#L172-L200):

```rust theme={null}
pub struct EndpointPattern {
    /// Hostname pattern (e.g., "api.example.com", "*.example.com")
    pub host: String,
    
    /// Optional path prefix (e.g., "/v1/", "/api/")
    /// None = all paths allowed
    pub path_prefix: Option<String>,
    
    /// Allowed HTTP methods (empty = all methods allowed)
    pub methods: Vec<String>,
}

impl EndpointPattern {
    pub fn host(host: impl Into<String>) -> Self {
        Self {
            host: host.into(),
            path_prefix: None,
            methods: Vec::new(),
        }
    }
    
    pub fn with_path_prefix(mut self, prefix: impl Into<String>) -> Self {
        self.path_prefix = Some(prefix.into());
        self
    }
    
    pub fn with_methods(mut self, methods: Vec<String>) -> Self {
        self.methods = methods;
        self
    }
}
```

### Validation Logic

From [src/tools/wasm/allowlist.rs:74-164](~/workspace/source/src/tools/wasm/allowlist.rs#L74-L164):

```rust theme={null}
pub struct AllowlistValidator {
    patterns: Vec<EndpointPattern>,
    require_https: bool,  // Default: true
}

pub fn validate(&self, url: &str, method: &str) -> AllowlistResult {
    // 1. Check for empty allowlist
    if self.patterns.is_empty() {
        return AllowlistResult::Denied(DenyReason::EmptyAllowlist);
    }
    
    // 2. Parse URL
    let parsed = parse_url(url)?;
    
    // 3. Check HTTPS requirement
    if self.require_https && parsed.scheme != "https" {
        return AllowlistResult::Denied(DenyReason::InsecureScheme(parsed.scheme));
    }
    
    // 4. Find matching pattern
    for pattern in &self.patterns {
        if pattern.matches(&parsed.host, &parsed.path, method) {
            return AllowlistResult::Allowed;
        }
    }
    
    // 5. Generate specific denial reason
    if no_host_match {
        AllowlistResult::Denied(DenyReason::HostNotAllowed(parsed.host))
    } else if no_path_match {
        AllowlistResult::Denied(DenyReason::PathNotAllowed { host, path })
    } else {
        AllowlistResult::Denied(DenyReason::MethodNotAllowed { method, host })
    }
}
```

### Host Matching

#### Exact Match

```json theme={null}
{"host": "api.openai.com"}
```

Matches:

* ✓ `https://api.openai.com/v1/chat`
* ❌ `https://openai.com/v1/chat` (different host)
* ❌ `https://api.openai.com.evil.com` (suffix attack)

#### Wildcard Subdomain

```json theme={null}
{"host": "*.example.com"}
```

Matches:

* ✓ `https://api.example.com/data`
* ✓ `https://staging.example.com/data`
* ❌ `https://example.com/data` (no subdomain)
* ❌ `https://api.example.com.evil.com` (suffix attack blocked)

#### Case-Insensitive

```json theme={null}
{"host": "API.OpenAI.com"}
```

Matches:

* ✓ `https://api.openai.com`
* ✓ `https://API.OPENAI.COM`
* ✓ `https://Api.OpenAi.Com`

All hosts normalized to lowercase before matching.

### Path Prefix Matching

#### With Prefix

```json theme={null}
{
  "host": "api.github.com",
  "path_prefix": "/repos/"
}
```

Matches:

* ✓ `https://api.github.com/repos/owner/repo`
* ✓ `https://api.github.com/repos/owner/repo/issues`
* ❌ `https://api.github.com/users/username`
* ❌ `https://api.github.com/user/repos` (different prefix)

#### Without Prefix

```json theme={null}
{
  "host": "httpbin.org"
}
```

Matches:

* ✓ `https://httpbin.org/get`
* ✓ `https://httpbin.org/post`
* ✓ `https://httpbin.org/anything/goes/here`

All paths allowed when `path_prefix` is `null`.

### Method Filtering

#### Specific Methods

```json theme={null}
{
  "host": "api.example.com",
  "methods": ["GET", "POST"]
}
```

Allows:

* ✓ GET requests
* ✓ POST requests
* ❌ PUT requests
* ❌ DELETE requests

#### All Methods

```json theme={null}
{
  "host": "api.example.com",
  "methods": []
}
```

Allows:

* ✓ GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS, etc.

Empty `methods` array means all methods allowed.

### Security Edge Cases

#### Userinfo in URL (Blocked)

```
https://user:pass@api.openai.com/v1/chat
```

Blocked to prevent:

* Credential leakage in URLs
* Host confusion attacks (`https://trusted@evil.com`)

From [src/tools/wasm/allowlist.rs:174-198](~/workspace/source/src/tools/wasm/allowlist.rs#L174-L198):

```rust theme={null}
fn parse_url(url: &str) -> Result<ParsedUrl> {
    let parsed = url::Url::parse(url)?;
    
    // Reject URLs with userinfo (user:pass@host)
    if !parsed.username().is_empty() || parsed.password().is_some() {
        return Err("URL contains userinfo (@) which is not allowed");
    }
    
    // ... rest of parsing
}
```

#### Path Normalization

All paths normalized before matching:

```
https://api.example.com/v1/../admin/users
  → Normalized: /admin/users
  → Matched against path_prefix: "/v1/"
  → Result: ❌ Denied (bypassed prefix)
```

Prevents path traversal bypasses.

#### IPv6 Addresses

```
https://[2001:db8::1]/api/data
```

Brackets stripped before matching:

* `[2001:db8::1]` → `2001:db8::1`

## Rate Limiting

Per-tool request limits prevent abuse and runaway costs.

### Rate Limit Configuration

From [src/tools/wasm/capabilities.rs](~/workspace/source/src/tools/wasm/capabilities.rs):

```rust theme={null}
pub struct RateLimitConfig {
    /// Maximum requests per minute
    pub requests_per_minute: u32,
    
    /// Maximum requests per hour
    pub requests_per_hour: u32,
}

impl Default for RateLimitConfig {
    fn default() -> Self {
        Self {
            requests_per_minute: 60,
            requests_per_hour: 1000,
        }
    }
}
```

Capabilities file:

```json theme={null}
{
  "http": {
    "rate_limit": {
      "requests_per_minute": 30,
      "requests_per_hour": 500
    }
  }
}
```

### Rate Limiter Implementation

Slidding window algorithm:

```rust:src/tools/wasm/rate_limiter.rs theme={null}
pub struct RateLimiter {
    minute_window: RateWindow,
    hour_window: RateWindow,
}

struct RateWindow {
    count: u32,
    window_start: Instant,
    window_duration: Duration,
}

impl RateLimiter {
    pub fn check_and_record(&mut self) -> RateLimitResult {
        let now = Instant::now();
        
        // Reset windows if expired
        if now - self.minute_window.window_start > Duration::from_secs(60) {
            self.minute_window.count = 0;
            self.minute_window.window_start = now;
        }
        
        // Check limits
        if self.minute_window.count >= self.config.requests_per_minute {
            return RateLimitResult::Denied(LimitType::PerMinute);
        }
        
        if self.hour_window.count >= self.config.requests_per_hour {
            return RateLimitResult::Denied(LimitType::PerHour);
        }
        
        // Increment counters
        self.minute_window.count += 1;
        self.hour_window.count += 1;
        
        RateLimitResult::Allowed
    }
}
```

### Rate Limit Errors

```rust theme={null}
pub enum RateLimitError {
    PerMinuteExceeded { limit: u32, window_reset: Instant },
    PerHourExceeded { limit: u32, window_reset: Instant },
}
```

WASM receives:

```json theme={null}
{
  "error": "RateLimitExceeded",
  "limit_type": "per_minute",
  "limit": 60,
  "reset_in_seconds": 42
}
```

## Size Limits

Prevent memory exhaustion and exfiltration via large payloads.

### Request Size Limits

```rust:src/tools/wasm/capabilities.rs theme={null}
pub struct HttpCapability {
    /// Maximum request body size (default: 1 MB)
    pub max_request_bytes: usize,
    
    /// Maximum response body size (default: 10 MB)
    pub max_response_bytes: usize,
}

impl Default for HttpCapability {
    fn default() -> Self {
        Self {
            max_request_bytes: 1024 * 1024,       // 1 MB
            max_response_bytes: 10 * 1024 * 1024, // 10 MB
            // ...
        }
    }
}
```

Validation:

```rust theme={null}
if body.len() > http_capability.max_request_bytes {
    return Err(WasmError::RequestTooLarge {
        size: body.len(),
        limit: http_capability.max_request_bytes,
    });
}
```

### Response Size Limits

```rust theme={null}
let response = reqwest::get(url).await?;
let body_bytes = response.bytes().await?;

if body_bytes.len() > http_capability.max_response_bytes {
    return Err(WasmError::ResponseTooLarge {
        size: body_bytes.len(),
        limit: http_capability.max_response_bytes,
    });
}
```

Prevents:

* **Memory exhaustion**: Large responses filling WASM memory
* **Cost overruns**: Downloading massive files
* **Exfiltration**: Uploading huge datasets

## HTTPS Enforcement

Plaintext HTTP is **blocked by default**.

```rust theme={null}
if self.require_https && parsed.scheme != "https" {
    return AllowlistResult::Denied(DenyReason::InsecureScheme("http"));
}
```

Attempts:

```
http://api.example.com/data  → ❌ Denied (insecure scheme)
https://api.example.com/data → ✓ Allowed (if in allowlist)
```

### Disable HTTPS Requirement (Not Recommended)

For local development/testing only:

```rust theme={null}
let validator = AllowlistValidator::new(patterns)
    .allow_http();  // ⚠️ Use with extreme caution
```

Never disable HTTPS in production.

## Timeout Controls

Prevent hanging requests.

```rust:src/tools/wasm/capabilities.rs theme={null}
pub struct HttpCapability {
    /// Request timeout (default: 30 seconds)
    pub timeout: Duration,
}

impl Default for HttpCapability {
    fn default() -> Self {
        Self {
            timeout: Duration::from_secs(30),
            // ...
        }
    }
}
```

Enforced via `reqwest`:

```rust theme={null}
let client = reqwest::Client::builder()
    .timeout(http_capability.timeout)
    .build()?;
```

After timeout:

```rust theme={null}
WasmError::HttpTimeout {
    url: "https://slow-api.example.com",
    timeout_secs: 30,
}
```

## Complete Example: OpenAI Integration

### Capabilities File

```json:openai_tool.capabilities.json theme={null}
{
  "http": {
    "allowlist": [
      {
        "host": "api.openai.com",
        "path_prefix": "/v1/",
        "methods": ["POST"]
      }
    ],
    "credentials": [
      {
        "secret_name": "openai_api_key",
        "location": {"AuthorizationBearer": {}},
        "host_patterns": ["api.openai.com"]
      }
    ],
    "rate_limit": {
      "requests_per_minute": 20,
      "requests_per_hour": 500
    },
    "max_request_bytes": 524288,   // 512 KB
    "max_response_bytes": 1048576, // 1 MB
    "timeout_secs": 30
  },
  "secrets": {
    "allowed_names": ["openai_api_key"]
  }
}
```

### Allowed Request

```rust theme={null}
// WASM code
let response = http_request(
    "POST",
    "https://api.openai.com/v1/chat/completions",
    vec![("Content-Type".to_string(), "application/json".to_string())],
    br#"{"model":"gpt-4","messages":[...]}".to_vec()
)?;
```

Validation flow:

1. ✓ Host `api.openai.com` in allowlist
2. ✓ Path `/v1/chat/completions` matches prefix `/v1/`
3. ✓ Method `POST` is allowed
4. ✓ Scheme is `https`
5. ✓ No secrets leaked in request
6. ✓ Rate limit not exceeded
7. ✓ Request body \< 512 KB
8. ✓ Credential injected: `Authorization: Bearer sk-...`
9. ✓ HTTP request executed
10. ✓ Response body \< 1 MB
11. ✓ No secrets in response
12. ✓ Response returned to WASM

### Denied Requests

#### Wrong Host

```rust theme={null}
http_request("GET", "https://evil.com/steal", ...)
```

❌ Denied: `HostNotAllowed("evil.com")`

#### Wrong Path

```rust theme={null}
http_request("GET", "https://api.openai.com/admin/users", ...)
```

❌ Denied: `PathNotAllowed { host: "api.openai.com", path: "/admin/users" }`

#### Wrong Method

```rust theme={null}
http_request("DELETE", "https://api.openai.com/v1/models", ...)
```

❌ Denied: `MethodNotAllowed { method: "DELETE", host: "api.openai.com" }`

#### Insecure Scheme

```rust theme={null}
http_request("GET", "http://api.openai.com/v1/models", ...)
```

❌ Denied: `InsecureScheme("http")`

#### Rate Limit Exceeded

```rust theme={null}
for _ in 0..100 {
    http_request("POST", "https://api.openai.com/v1/chat/completions", ...)
}
```

❌ After 20 requests in 1 minute: `RateLimitExceeded(PerMinute)`

#### Request Too Large

```rust theme={null}
let huge_body = vec![0u8; 10_000_000]; // 10 MB
http_request("POST", "https://api.openai.com/v1/...", ..., huge_body)
```

❌ Denied: `RequestTooLarge { size: 10000000, limit: 524288 }`

## Monitoring and Logging

All network activity is logged:

### Allowed Requests

```rust theme={null}
tracing::info!(
    tool = "openai_tool",
    method = "POST",
    url = "https://api.openai.com/v1/chat/completions",
    "HTTP request allowed"
);
```

### Denied Requests

```rust theme={null}
tracing::warn!(
    tool = "suspicious_tool",
    url = "https://evil.com/exfil",
    reason = "host_not_allowed",
    "HTTP request denied"
);
```

### Rate Limit Hits

```rust theme={null}
tracing::warn!(
    tool = "chatty_tool",
    limit_type = "per_minute",
    limit = 60,
    "Rate limit exceeded"
);
```

### Secret Leaks Blocked

```rust theme={null}
tracing::error!(
    tool = "leaky_tool",
    pattern = "openai_api_key",
    location = "request_url",
    "Secret leak blocked in HTTP request"
);
```

Enable debug logging:

```bash theme={null}
RUST_LOG=ironclaw::tools::wasm=debug ironclaw
```

## Best Practices

### For Tool Authors

1. **Request minimal access**

```json theme={null}
// ❌ Too broad
{"host": "*.com"}

// ✓ Specific
{"host": "api.specific-service.com", "path_prefix": "/v1/"}
```

2. **Use specific methods**

```json theme={null}
// ❌ All methods
{"methods": []}

// ✓ Only what you need
{"methods": ["GET", "POST"]}
```

3. **Set conservative limits**

```json theme={null}
{
  "rate_limit": {
    "requests_per_minute": 10,  // Start low, increase if needed
    "requests_per_hour": 100
  }
}
```

4. **Handle rate limits gracefully**

```rust theme={null}
match http_request(...) {
    Err(WasmError::RateLimitExceeded { reset_in_seconds, .. }) => {
        return format!("Rate limit hit, retry in {} seconds", reset_in_seconds);
    }
}
```

### For System Administrators

1. **Audit allowlists regularly**

```bash theme={null}
# Review all granted endpoints
find ~/.ironclaw/tools -name '*.capabilities.json' -exec jq .http.allowlist {} \;
```

2. **Monitor denied requests**

```bash theme={null}
# Watch for suspicious activity
RUST_LOG=warn ironclaw 2>&1 | grep "HTTP request denied"
```

3. **Set resource limits per tool**

```json theme={null}
{
  "http": {
    "max_request_bytes": 102400,   // 100 KB (smaller than default)
    "max_response_bytes": 1048576, // 1 MB
    "timeout_secs": 10             // Shorter timeout
  }
}
```

4. **Use different limits for different tools**

```json theme={null}
// High-volume tool
{"rate_limit": {"requests_per_minute": 120, "requests_per_hour": 5000}}

// Low-volume admin tool
{"rate_limit": {"requests_per_minute": 5, "requests_per_hour": 50}}
```

## Network Security Checklist

* [ ] Allowlist contains **only** necessary hosts
* [ ] Path prefixes are as **specific** as possible
* [ ] HTTP methods are **restricted** (not empty array)
* [ ] HTTPS is **enforced** (don't call `.allow_http()`)
* [ ] Rate limits are **appropriate** for use case
* [ ] Request/response sizes are **reasonable**
* [ ] Timeout is **not too long** (default 30s is good)
* [ ] Credentials are **only** for allowed hosts
* [ ] Logs are **monitored** for denied requests
* [ ] Capabilities are **reviewed** before deploying tools

## Source Code References

* **Allowlist validator**: [src/tools/wasm/allowlist.rs:74-164](~/workspace/source/src/tools/wasm/allowlist.rs#L74-L164)
* **HTTP capability**: [src/tools/wasm/capabilities.rs:102-169](~/workspace/source/src/tools/wasm/capabilities.rs#L102-L169)
* **Endpoint patterns**: [src/tools/wasm/capabilities.rs:172-200](~/workspace/source/src/tools/wasm/capabilities.rs#L172-L200)
* **Rate limiter**: [src/tools/wasm/rate\_limiter.rs](~/workspace/source/src/tools/wasm/rate_limiter.rs)
* **URL parsing**: [src/tools/wasm/allowlist.rs:174-198](~/workspace/source/src/tools/wasm/allowlist.rs#L174-L198)

## See Also

* [Security Overview](/security/overview) - Complete security architecture
* [WASM Sandbox](/security/wasm-sandbox) - HTTP capability configuration
* [Credential Management](/security/credentials) - Secret injection at boundary
* [Prompt Injection Defense](/security/prompt-injection) - External content safety
