🔵 JavaScript SDK v1.0

CloFix JavaScript (clofix) Scripting Guide

Write clofix JavaScript functions to inspect, filter, and control every HTTP request flowing through CloFix WAF — with full access to request context, shared in-memory state, pattern extraction, and comprehensive OWASP Top 10 protection.

function clofix(request) Return action object Shared dictionaries Pattern extraction SQLi / XSS / CMDi Rate limiting GeoIP blocking Bot detection Tor / VPN blocking OWASP Top 10 2 second timeout

01 Introduction

CloFix WAF executes JavaScript (clofix) scripts on every HTTP request before it reaches your backend. Scripts have read access to a rich request object and read/write access to shared in-memory dictionaries for maintaining state across requests.

⚙️ CloFix WAF JavaScript Architecture
HTTP Request JavaScript VM
(Isolated per script)
Shared Dicts
(State / Rate Limit)
Action / Backend
Important — Entry Point Every executable JavaScript file must export a function clofix(request) that returns an action object. The WAF will call this function for every request.

Key Concepts

  • Scripts live in /etc/clofix/<domain>/js/ — one directory per domain
  • All .js files run in filesystem order; first blocking action wins
  • Every script must export function clofix(request)
  • Scripts return an object with an action property
  • Shared dictionaries provide persistent state across requests using sharedDict("name")
  • Each script runs in an isolated JavaScript VM with a 2 second wall-clock timeout
  • Patterns are automatically extracted from ALL JavaScript files for fast matching

🛡️ OWASP Top 10 Coverage

A01:2021-Broken Access Control Path/IP whitelisting, admin area protection, API key auth
A02:2021-Cryptographic Failures Enforce HTTPS, detect missing security headers
A03:2021-Injection SQLi, NoSQLi, CMDi, LDAP via regex pattern matching
A04:2021-Insecure Design Rate limiting, brute-force protection, bot mitigation
A05:2021-Security Misconfiguration Block debug paths, .git, .env, directory listing attempts
A06:2021-Vulnerable Components Detect scanner UA (nikto, nuclei) and known exploit paths
A07:2021-Identification/Auth Failures Credential stuffing detection, MFA bypass monitoring
A08:2021-Software/Data Integrity Prevent malicious file uploads, block webshell extensions
A09:2021-Security Logging/Monitoring Structured attack logging with full request context
A10:2021-SSRF Block internal IPs/hostnames in URL parameters

02 Execution Flow

Every HTTP request passes through a fixed pipeline before reaching your backend.

pipeline
  HTTP Request
       │
       ▼
  ┌─────────────────────────────────────────────┐
  │  WAF Engine  — IP extraction, GeoIP, TLS    │
  │  fingerprint, bot detection                 │
  └───────────────────┬─────────────────────────┘
                       │  enriched request object
                       ▼
  ┌─────────────────────────────────────────────┐
  │  JavaScript Script Pipeline (per domain)    │
  │                                             │
  │  01_rate_limit.js   ──►  action object      │
  │       │ allow                               │
  │  02_bot_detect.js   ──►  action object      │
  │       │ allow                               │
  │  03_sql_injection.js ──► action object      │
  │                                             │
  │  First non-allow action WINS — pipeline     │
  │  stops immediately.                         │
  └───────────────────┬─────────────────────────┘
                       │
              ┌────────┴────────┐
              │                 │
           block /          allow
         rate_limit              │
              │                  ▼
              ▼           Backend / Proxy
        Error Response
        (403 / 429 / etc)

03 Quick Reference

Request Object — all fields

javascript — request object
// ── Core ──────────────────────────────────────────────
request.ip              // "203.0.113.5"      client IP
request.method          // "GET" / "POST" …
request.path            // "/page"            path only
request.url             // "/page?id=1"       full URI
request.host            // "example.com"
request.query           // { id: "1", page: "2" } parsed query

// ── Headers & Body ───────────────────────────────────
request.headers         // { "user-agent": "...", "accept": "..." }
request.body            // raw request body string
request.cookies         // { session_id: "...", csrf: "..." }
request.user_agent      // "Mozilla/5.0 …"
request.referer         // "https://google.com/…"
request.content_type    // "application/json"
request.content_length  // 1024  (bytes)

// ── Security signals ───────────────────────────────────
request.is_tor          // true if Tor exit node
request.timestamp        // 1709123456          Unix timestamp

Return Object — full structure

javascript — return object
return {
    action:      "block",          // required: "allow", "block", "rate_limit", "redirect"
    status:      403,             // HTTP status (auto-filled if omitted)
    reason:      "SQL Injection detected",  // human-readable reason
    rule_name:   "SQLI_001",      // rule identifier — appears in WAF logs
    category:    "sql_injection", // attack category
    confidence:  95,              // confidence score (0-100)
    severity:    "high",          // "critical", "high", "medium", "low"
    redirect_to: "",              // target URL when action == "redirect"
}

04 Directory Structure

JavaScript files must be placed in the domain-specific JS directory:

filesystem
/etc/clofix/
├── example.com/
│   └── js/
│       ├── 01_rate_limit.js
│       ├── 02_bot_detection.js
│       ├── 03_sql_injection.js
│       └── 04_custom_rules.js
├── api.example.com/
│   └── js/
│       └── api_security.js
└── shop.example.com/
    └── js/
        └── geo_block.js
Execution Order Files execute in filesystem (alphabetical) order. Use numeric prefixes like 01_, 02_ to control priority.

05 Domain Configuration

Enable JavaScript scripting in your domain configuration:

/etc/clofix/conf/example.com.conf
domain example.com {
    backend http://localhost:8080;
    
    # JavaScript Blocker Configuration
    js_blocker {
        enabled on;
        js_dir /etc/clofix/example.com/js;
        scan_interval 5m;
        block_threshold 70;
        max_file_size 1MB;
        enable_logging on;
        enable_execution on;
        execution_timeout 2s;
        include_extensions .js,.mjs,.cjs;
    }
}

06 Script Structure

Minimal Script

javascript
function clofix(request) {
    // Your logic here
    return { action: "allow" };
}

Full Template

javascript
// Configuration constants
const CONFIG = {
    ENABLED: true,
    RATE_LIMIT: 100,
    BLOCK_TIME: 300
};

// Helper functions
function isWhitelisted(ip) {
    const whitelist = { "192.168.1.1": true };
    return whitelist[ip] === true;
}

// Main entry point — must be named clofix
function clofix(request) {
    if (!CONFIG.ENABLED) {
        return { action: "allow" };
    }

    const ip = request.ip;
    if (isWhitelisted(ip)) {
        return { action: "allow" };
    }

    // ... security logic ...

    return { action: "allow" };
}

07 Action System

The clofix function must return an object with at minimum an action property.

Supported Action Types

allow
Pass request to backend (status 200)
block
Reject immediately (status 403)
rate_limit
Rate limit response (status 429)
redirect
HTTP redirect using redirect_to

Examples

javascript — action examples
// Block with full metadata
return {
    action: "block",
    status: 403,
    reason: "SQL Injection detected",
    rule_name: "SQLI_001",
    category: "sql_injection",
    confidence: 95
};

// Rate limit
return {
    action: "rate_limit",
    status: 429,
    reason: "Too many requests",
    rule_name: "RL_001"
};

// Redirect
return {
    action: "redirect",
    status: 301,
    redirect_to: "https://example.com/new-path"
};

08 Request Object Reference

PropertyTypeDescriptionExample
ipstringClient IP address"203.0.113.5"
methodstringHTTP method"GET", "POST"
pathstringURI path only"/api/users"
urlstringFull URL with query"/api/users?id=1"
hoststringHost header"example.com"
queryobjectParsed query parameters{ id: "1", page: "2" }
headersobjectAll HTTP headers{ "user-agent": "..." }
bodystringRequest body"{\"name\":\"test\"}"
cookiesobjectAll cookies{ session_id: "abc123" }
user_agentstringUser-Agent header"Mozilla/5.0 ..."
refererstringReferer header"https://google.com"
content_typestringContent-Type header"application/json"
content_lengthnumberContent length1024
is_torbooleanTor exit nodetrue/false
timestampnumberUnix timestamp1709123456

09 Shared Dictionaries

Shared dictionaries maintain state across requests. Access them using the global sharedDict function.

Important: JavaScript Syntax In JavaScript/Goja, use sharedDict.get() and sharedDict.set() - not shared.ddos_attack syntax.
javascript — shared dictionary API
// CORRECT SYNTAX - Using sharedDict function
const dict = sharedDict("ddos_attack");  // Get dictionary by name

// Available methods
dict.get("key")                 // Get value (returns undefined if missing)
dict.set("key", value)           // Set value (no expiration)
dict.set("key", value, ttl)       // Set with TTL in seconds
dict.incr("key", delta)          // Atomically increment a number
dict.delete("key")               // Remove a key
dict.ttl("key")                  // Get remaining TTL in seconds
dict.flush_all()                   // Clear all keys

// Example: Rate limiting with TTL
function clofix(request) {
    const dict = sharedDict("ddos_attack");
    const key = "rate:" + request.ip;
    
    // Check if currently blocked
    const blocked = dict.get("block:" + key) || 0;
    const now = Math.floor(Date.now() / 1000);
    
    if (blocked > now) {
        return { action: "rate_limit" };
    }
    
    // Increment counter (expires after 60 seconds)
    let count = dict.incr(key, 1);
    dict.set(key, count, 60);
    
    if (count > 100) {
        // Block for 5 minutes
        dict.set("block:" + key, now + 300, 300);
        return { action: "rate_limit" };
    }
    
    return { action: "allow" };
}

10 Rate Limiting

Implement rate limiting using shared dictionaries:

javascript — rate_limit.js
const CONFIG = {
    REQUESTS_PER_MINUTE: 60,
    BLOCK_TIME: 300  // 5 minutes
};

function clofix(request) {
    const dict = sharedDict("ddos_attack");
    const ip = request.ip;
    const now = Math.floor(Date.now() / 1000);
    
    // Check if currently blocked
    const blocked = dict.get("block:" + ip) || 0;
    if (blocked > now) {
        return {
            action: "rate_limit",
            reason: "Temporarily blocked due to rate limit"
        };
    }
    
    // FIXED: Use timestamp array for accurate counting
    const key = "rate:" + ip;
    
    // Get existing timestamps
    let timestamps = dict.get(key);
    if (!timestamps) {
        timestamps = [];
    } else {
        try {
            timestamps = JSON.parse(timestamps);
        } catch (e) {
            timestamps = [];
        }
    }
    
    // Remove timestamps older than 60 seconds
    const cutoff = now - 60;
    timestamps = timestamps.filter(ts => ts > cutoff);
    
    // Check if over limit
    if (timestamps.length >= CONFIG.REQUESTS_PER_MINUTE) {
        // Block for 5 minutes
        dict.set("block:" + ip, now + CONFIG.BLOCK_TIME, CONFIG.BLOCK_TIME);
        dict.delete(key);
        return {
            action: "rate_limit",
            reason: "Rate limit exceeded",
            rule_name: "RL_001"
        };
    }
    
    // Add current timestamp
    timestamps.push(now);
    dict.set(key, JSON.stringify(timestamps), 70);
    
    return { action: "allow" };
}

11 Pattern Extraction

The system automatically extracts patterns from ALL JavaScript files, not just executable ones. These patterns are used for fast matching without execution.

javascript — patterns.js (auto-extracted)
// Strings in this file will be extracted as patterns
const maliciousPatterns = [
    "eval(atob(",
    "document.write(String.fromCharCode",
    "coinhive.min.js",
    "cryptonight.wasm"
];

// Regex patterns will also be extracted
const sqlPatterns = [
    /union.*select/i,
    /select.*from/i,
    /insert.*into/i
];

// Function names become patterns too
function detectSQLInjection(input) {
    // This function name "detectSQLInjection" becomes a pattern
    return sqlPatterns.some(p => p.test(input));
}
Pattern Matching All extracted patterns are checked against every request (URL, headers, body, cookies). If confidence exceeds threshold, the request is blocked.

12 Logging

Use the global log object for structured logging:

javascript — logging
function clofix(request) {
    // Simple log messages
    log.info("Processing request from " + request.ip);
    log.warn("Suspicious activity detected");
    log.error("Script error occurred");
    
    // Structured attack logging
    log.attack({
        rule_name: "SQLI_001",
        category: "sql_injection",
        confidence: 95,
        payload: request.body.substring(0, 100)
    });
    
    return { action: "allow" };
}

13 Execution Limits

LimitDefaultPurpose
Wall-clock timeout2 secondsScript that takes longer is killed and the request is blocked with 503
Memory32 MBMemory limit per script execution
File size10 MBMaximum JavaScript file size processed
Timeout Behaviour If a script exceeds 2 seconds the WAF returns HTTP 503. Keep pattern matching efficient and avoid heavy loops.

14 OWASP Top 10 Examples

SQL Injection (A03:2021)

sql_injection.js
const SQL_PATTERNS = [
    /union.*select/i,
    /select.*from/i,
    /insert.*into/i,
    /update.*set/i,
    /delete.*from/i,
    /drop.*table/i,
    /--/,
    /#/,
    /\/\*/,
    /or\s+1\s*=\s*1/i,
    /sleep\s*\(/i
];

function clofix(request) {
    // Check URL
    for (let pattern of SQL_PATTERNS) {
        if (pattern.test(request.url)) {
            return {
                action: "block",
                reason: "SQL Injection detected in URL",
                category: "sql_injection",
                confidence: 90
            };
        }
    }
    
    // Check query parameters
    if (request.query) {
        for (let [key, value] of Object.entries(request.query)) {
            for (let pattern of SQL_PATTERNS) {
                if (pattern.test(value)) {
                    return {
                        action: "block",
                        reason: `SQL Injection in parameter: ${key}`,
                        category: "sql_injection",
                        confidence: 95
                    };
                }
            }
        }
    }
    
    return { action: "allow" };
}

Cross-Site Scripting (XSS) (A03:2021)

xss_detection.js
const XSS_PATTERNS = [
    /<script\b[^>]*>.*?<\/script>/i,
    /javascript:/i,
    /onerror\s*=/i,
    /onload\s*=/i,
    /onclick\s*=/i,
    /onmouseover\s*=/i,
    /alert\s*\(/i,
    /eval\s*\(/i,
    /document\.cookie/i,
    /<iframe\b/i,
    /<img\b[^>]*onerror/i
];

function clofix(request) {
    const inputs = [
        request.url,
        request.body,
        ...Object.values(request.query || {}),
        ...Object.values(request.cookies || {}),
        ...Object.values(request.headers || {})
    ];
    
    for (let input of inputs) {
        if (typeof input !== 'string') continue;
        
        for (let pattern of XSS_PATTERNS) {
            if (pattern.test(input)) {
                return {
                    action: "block",
                    reason: "XSS attack detected",
                    category: "xss",
                    confidence: 90
                };
            }
        }
    }
    
    return { action: "allow" };
}

Path Traversal (A01:2021)

path_traversal.js
const TRAVERSAL_PATTERNS = [
    /\.\.\//g,
    /\.\.\\/g,
    /\%2e\%2e\%2f/gi,
    /\%2e\%2e\%5c/gi,
    /etc\/passwd/i,
    /etc\/shadow/i,
    /windows\\system32/i,
    /boot\.ini/i
];

function clofix(request) {
    // Check URL path
    if (request.path) {
        for (let pattern of TRAVERSAL_PATTERNS) {
            if (pattern.test(request.path)) {
                return {
                    action: "block",
                    reason: "Path traversal detected",
                    category: "path_traversal",
                    confidence: 95
                };
            }
        }
    }
    
    // Check query parameters
    if (request.query) {
        for (let [key, value] of Object.entries(request.query)) {
            for (let pattern of TRAVERSAL_PATTERNS) {
                if (pattern.test(value)) {
                    return {
                        action: "block",
                        reason: `Path traversal in parameter: ${key}`,
                        category: "path_traversal",
                        confidence: 95
                    };
                }
            }
        }
    }
    
    return { action: "allow" };
}

Command Injection (A03:2021)

cmd_injection.js
const CMD_PATTERNS = [
    /;\s*(ls|dir|cat|echo|rm|del|cp|mv|ps|kill)/i,
    /\|\s*(ls|dir|cat|echo|grep)/i,
    /&&\s*(ls|dir|cat|echo)/i,
    /`.*`/g,
    /\$\(.*\)/g,
    /(ping|nslookup|traceroute|tracert)\s+[0-9.-]+/i,
    /(wget|curl)\s+http/i,
    /\/bin\/sh/i,
    /\/bin\/bash/i
];

function clofix(request) {
    const inputs = [
        request.url,
        request.body,
        ...Object.values(request.query || {}),
        ...Object.values(request.headers || {})
    ];
    
    for (let input of inputs) {
        if (typeof input !== 'string') continue;
        
        for (let pattern of CMD_PATTERNS) {
            if (pattern.test(input)) {
                return {
                    action: "block",
                    reason: "Command injection detected",
                    category: "command_injection",
                    confidence: 95
                };
            }
        }
    }
    
    return { action: "allow" };
}

Server-Side Request Forgery (SSRF) (A10:2021)

ssrf_protection.js
const SSRF_PATTERNS = [
    /(?:https?|ftp|file):\/\/(?:127\.0\.0\.1|localhost|169\.254\.|192\.168\.|10\.|172\.(?:1[6-9]|2[0-9]|3[01]))/i,
    /(?:https?|ftp|file):\/\/(?:0\.0\.0\.0|::1|metadata\.google\.internal|169\.254\.169\.254)/i,
    /(?:https?|ftp|file):\/\/(?:.*\.internal|.*\.local|.*\.localhost)/i,
    /(?:https?|ftp|file):\/\/(?:.*\.amazonaws\.com\/latest\/meta-data)/i
];

function clofix(request) {
    // Check URL parameters
    if (request.query) {
        for (let [key, value] of Object.entries(request.query)) {
            if (typeof value === 'string') {
                for (let pattern of SSRF_PATTERNS) {
                    if (pattern.test(value)) {
                        return {
                            action: "block",
                            reason: `SSRF attack in parameter: ${key}`,
                            category: "ssrf",
                            confidence: 95
                        };
                    }
                }
            }
        }
    }
    
    return { action: "allow" };
}

Cross-Site Request Forgery (CSRF) (A07:2021)

csrf_protection.js
const CONFIG = {
    ALLOWED_ORIGINS: ["https://example.com", "https://www.example.com"],
    EXEMPT_PATHS: ["/api/webhook", "/api/public"]
};

function clofix(request) {
    const protectedMethods = { "POST": true, "PUT": true, "DELETE": true, "PATCH": true };
    if (!protectedMethods[request.method]) {
        return { action: "allow" };
    }
    
    for (let path of CONFIG.EXEMPT_PATHS) {
        if (request.path.startsWith(path)) {
            return { action: "allow" };
        }
    }
    
    const origin = request.headers["origin"] || "";
    if (origin) {
        let allowed = false;
        for (let o of CONFIG.ALLOWED_ORIGINS) {
            if (origin === o) {
                allowed = true;
                break;
            }
        }
        if (!allowed) {
            return {
                action: "block",
                reason: "CSRF: Invalid Origin",
                category: "csrf",
                confidence: 90
            };
        }
    }
    
    return { action: "allow" };
}

15 Core Security Examples

GeoIP Country Blocking (requires GeoIP implementation)

geo_block.js
const BLOCKED_COUNTRIES = {
    "XX": true,  // Replace with actual country codes
    "YY": true
};

function clofix(request) {
    if (request.country && BLOCKED_COUNTRIES[request.country]) {
        return {
            action: "block",
            reason: `Access from ${request.country} is not allowed`,
            category: "geo_block",
            confidence: 100
        };
    }
    
    return { action: "allow" };
}

Bot Detection

bot_detection.js
const BOT_UA_PATTERNS = [
    "bot", "crawler", "spider", "scraper",
    "wget", "curl", "python", "go-http-client",
    "headless", "phantomjs", "puppeteer",
    "selenium", "playwright"
];

function clofix(request) {
    const ua = (request.user_agent || "").toLowerCase();
    
    if (!ua) {
        return {
            action: "challenge",
            reason: "Missing User-Agent",
            category: "bot",
            confidence: 70
        };
    }
    
    for (let pattern of BOT_UA_PATTERNS) {
        if (ua.includes(pattern)) {
            return {
                action: "block",
                reason: `Bot detected: ${pattern}`,
                category: "bot",
                confidence: 85
            };
        }
    }
    
    return { action: "allow" };
}

Path-Based IP Whitelist

path_protection.js
const PROTECTED = { "/admin": true, "/wp-admin": true };
const ALLOWED = { "192.168.1.100": true, "10.0.0.50": true };

function clofix(request) {
    for (let path in PROTECTED) {
        if (request.path.startsWith(path)) {
            if (!ALLOWED[request.ip]) {
                return {
                    action: "block",
                    reason: "Unauthorized access to protected path",
                    category: "access_control",
                    confidence: 95
                };
            }
        }
    }
    return { action: "allow" };
}

Brute Force Login Protection

brute_force.js
const CONFIG = {
    LOGIN_PATHS: ["/login", "/wp-login.php", "/api/auth"],
    MAX_ATTEMPTS: 5,
    WINDOW: 300,
    BAN_TIME: 1800
};

function clofix(request) {
    const dict = sharedDict("ddos_attack");
    const ip = request.ip;
    const now = Math.floor(Date.now() / 1000);
    
    let isLogin = false;
    for (let path of CONFIG.LOGIN_PATHS) {
        if (request.path.startsWith(path)) {
            isLogin = true;
            break;
        }
    }
    
    if (!isLogin) {
        return { action: "allow" };
    }
    
    const banned = dict.get("bf_ban:" + ip) || 0;
    if (banned > now) {
        return { action: "rate_limit", reason: "Too many failed login attempts" };
    }
    
    const key = "bf_cnt:" + ip;
    let count = dict.get(key) || 0;
    count = dict.incr(key, 1);
    dict.set(key, count, CONFIG.WINDOW);
    
    if (count >= CONFIG.MAX_ATTEMPTS) {
        dict.set("bf_ban:" + ip, now + CONFIG.BAN_TIME, CONFIG.BAN_TIME);
        dict.delete(key);
        return { action: "rate_limit", reason: "Rate limit exceeded", rule_name: "BF_001" };
    }
    
    return { action: "allow" };
}

DDoS Protection

ddos_shield.js
const CONFIG = {
    BURST_LIMIT: 30,
    SUSTAINED_LIMIT: 200,
    BAN1: 60,
    BAN2: 600,
    BAN3: 86400
};

function escalateBan(ip, now, dict, type) {
    const offenseKey = "ddos_off:" + ip;
    let offense = dict.get(offenseKey) || 0;
    offense = dict.incr(offenseKey, 1);
    dict.set(offenseKey, offense, 86400);
    
    let banTime;
    if (offense === 1) banTime = CONFIG.BAN1;
    else if (offense === 2) banTime = CONFIG.BAN2;
    else banTime = CONFIG.BAN3;
    
    dict.set("ddos_ban:" + ip, now + banTime, banTime);
    
    return { action: "rate_limit", reason: `DDoS ${type} detected`, rule_name: "DDOS_001" };
}

function clofix(request) {
    const dict = sharedDict("ddos_attack");
    const ip = request.ip;
    const now = Math.floor(Date.now() / 1000);
    
    const banned = dict.get("ddos_ban:" + ip) || 0;
    if (banned > now) {
        return { action: "rate_limit" };
    }
    
    const burstKey = "ddos_burst:" + ip;
    let burst = dict.get(burstKey) || 0;
    burst = dict.incr(burstKey, 1);
    dict.set(burstKey, burst, 2);
    
    if (burst > CONFIG.BURST_LIMIT) {
        return escalateBan(ip, now, dict, "burst");
    }
    
    const minuteKey = "ddos_min:" + ip + ":" + new Date().toISOString().substring(0, 16);
    let minute = dict.get(minuteKey) || 0;
    minute = dict.incr(minuteKey, 1);
    dict.set(minuteKey, minute, 60);
    
    if (minute > CONFIG.SUSTAINED_LIMIT) {
        return escalateBan(ip, now, dict, "sustained");
    }
    
    return { action: "allow" };
}

HTTP Method Filter

method_filter.js
const READ_ONLY_PATHS = ["/blog", "/docs", "/static"];

function clofix(request) {
    const method = request.method.toUpperCase();
    
    // Block dangerous methods
    const dangerous = { "TRACE": true, "TRACK": true, "CONNECT": true };
    if (dangerous[method]) {
        return { action: "block", status: 405, reason: "Method not allowed" };
    }
    
    // Read-only paths should only allow GET/HEAD
    for (let path of READ_ONLY_PATHS) {
        if (request.path.startsWith(path)) {
            if (method !== "GET" && method !== "HEAD") {
                return { action: "block", status: 405, reason: "Read-only path" };
            }
        }
    }
    
    return { action: "allow" };
}

Tor / VPN Block

tor_vpn_block.js
function clofix(request) {
    if (request.is_tor) {
        return {
            action: "block",
            reason: "Tor exit node access denied",
            category: "tor",
            confidence: 100
        };
    }
    
    return { action: "allow" };
}

16 More Useful Scripts

Password Protect Any URL

password_protect.js
const CONFIG = {
    SECRET: "change-me-strong-secret",
    PROTECTED_PATHS: ["/staging", "/preview", "/beta"],
    TRUSTED_IPS: { "192.168.1.0": true }
};

function clofix(request) {
    let protected = false;
    for (let p of CONFIG.PROTECTED_PATHS) {
        if (request.path.startsWith(p)) {
            protected = true;
            break;
        }
    }
    
    if (!protected || CONFIG.TRUSTED_IPS[request.ip]) {
        return { action: "allow" };
    }
    
    const token = request.query["token"];
    if (token === CONFIG.SECRET) {
        return { action: "allow" };
    }
    
    return { action: "block", status: 401, reason: "Authentication required" };
}

API Key Authentication

api_key_auth.js
const API_KEYS = {
    "key-client-alpha-1234": { name: "client-alpha", rate: 100 },
    "key-client-beta-5678":  { name: "client-beta",  rate: 500 }
};

const PROTECTED = ["/api/", "/v1/", "/v2/"];

function clofix(request) {
    let needs = false;
    for (let p of PROTECTED) {
        if (request.path.startsWith(p)) {
            needs = true;
            break;
        }
    }
    
    if (!needs) return { action: "allow" };
    
    const key = request.headers["x-api-key"] || request.query["api_key"];
    if (!key) {
        return { action: "block", status: 401, reason: "API key required" };
    }
    
    if (!API_KEYS[key]) {
        return { action: "block", status: 403, reason: "Invalid API key" };
    }
    
    return { action: "allow" };
}

Dangerous File Type Filter

file_type_filter.js
const BLOCKED_EXT = [
    ".php", ".php3", ".php5", ".phtml", ".asp", ".aspx",
    ".jsp", ".cgi", ".pl", ".py", ".sh", ".exe", ".bat", ".jar"
];

function clofix(request) {
    const path = request.path.toLowerCase();
    
    for (let ext of BLOCKED_EXT) {
        if (path.endsWith(ext)) {
            return {
                action: "block",
                reason: `File extension ${ext} not allowed`,
                category: "file_filter",
                confidence: 95
            };
        }
    }
    
    return { action: "allow" };
}

Honeypot Bot Trap

honeypot.js
const CONFIG = {
    BAN_TIME: 86400,
    TRAP_PATHS: ["/trap-link", "/do-not-visit", "/honeypot"],
    TRUSTED_AGENTS: ["googlebot", "bingbot"]
};

function clofix(request) {
    const dict = sharedDict("ddos_attack");
    const ip = request.ip;
    const now = Math.floor(Date.now() / 1000);
    const path = request.path.toLowerCase();
    const ua = (request.user_agent || "").toLowerCase();
    
    for (let trap of CONFIG.TRAP_PATHS) {
        if (path === trap) {
            let trusted = false;
            for (let a of CONFIG.TRUSTED_AGENTS) {
                if (ua.includes(a)) trusted = true;
            }
            if (!trusted) {
                dict.set("hp_ban:" + ip, now + CONFIG.BAN_TIME, CONFIG.BAN_TIME);
                return { action: "block", reason: "Honeypot triggered" };
            }
        }
    }
    
    return { action: "allow" };
}

Maintenance Mode

maintenance.js
const CONFIG = {
    ENABLED: true,
    ALLOWED_IPS: { "203.0.113.10": true },
    ALWAYS_ON: ["/health", "/ping", "/status"]
};

function clofix(request) {
    if (!CONFIG.ENABLED) return { action: "allow" };
    
    for (let p of CONFIG.ALWAYS_ON) {
        if (request.path === p) return { action: "allow" };
    }
    
    if (CONFIG.ALLOWED_IPS[request.ip]) {
        return { action: "allow" };
    }
    
    return { action: "block", status: 503, reason: "Under maintenance" };
}

Smart Redirects

redirects.js
const EXACT = {
    "/home": { to: "/", code: 301 },
    "/about-us": { to: "/about", code: 301 }
};

const PREFIX = [
    { from: "/old-blog/", to: "/blog/", code: 301 }
];

function clofix(request) {
    const path = request.path;
    
    if (EXACT[path]) {
        return {
            action: "redirect",
            status: EXACT[path].code,
            redirect_to: EXACT[path].to
        };
    }
    
    for (let r of PREFIX) {
        if (path.startsWith(r.from)) {
            const dest = r.to + path.substring(r.from.length);
            return {
                action: "redirect",
                status: r.code,
                redirect_to: dest
            };
        }
    }
    
    return { action: "allow" };
}

17 Testing

Diagnostic Script

debug.js — remove before production
function clofix(request) {
    log.info("=== DEBUG ===");
    log.info("IP: " + request.ip);
    log.info("Method: " + request.method);
    log.info("Path: " + request.path);
    log.info("UA: " + request.user_agent);
    log.info("Is Tor: " + request.is_tor);
    
    return { action: "allow" };
}

Shell Commands

shell
# Watch WAF logs
sudo tail -f /var/log/clofix/waf.log | grep -i '\[JSBlocker\]'

# Test normal request
curl https://your-domain.com/

# Test SQL injection
curl 'https://your-domain.com/?id=1%20UNION%20SELECT%20*%20FROM%20users'

# Test XSS
curl 'https://your-domain.com/?q=<script>alert(1)</script>'

# Test rate limiting
for i in $(seq 1 200); do
    curl -s -o /dev/null https://your-domain.com/ &
done
wait

# Test scanner detection
curl -A "sqlmap/1.7" https://your-domain.com/

18 Troubleshooting

Error / SymptomCauseFix
function clofix not foundFunction missing or misnamedExport exactly function clofix(request)
sharedDict is not definedShared dict not declared in configAdd lua_shared_dict <name> <size>m; to config
Cannot read property 'ip' of undefinedRequest object is undefinedFunction signature must be clofix(request)
JavaScript execution timed outScript exceeds 2 secondsOptimize loops, use shared dict for caching
SyntaxError: Unexpected tokenJavaScript syntax errorValidate with node -c script.js
Changes not taking effectFile not saved or scannedCheck file permissions, trigger rescan via API

19 Best Practices

  1. One script per concern — keep rate_limit.js, bot_detection.js, sql_injection.js separate
  2. Always validate inputs — use if (request.body && typeof request.body === 'string')
  3. Return early — check blocklists first before heavy pattern matching
  4. Use shared dictionaries with TTL — always set expiration times to prevent memory leaks
  5. Log attacks with context — use log.attack() for structured logging
  6. Test with diagnostic script first — deploy debug.js to verify your environment
  7. Numeric prefixes on filenames — use 01_rate_limit.js, 02_bot_detect.js to control order
  8. CONFIG objects at the top — put all tunable values in a CONFIG object for easy adjustments
  9. Remove debug logs in production — delete or comment out log.info() calls
Need Help? For CloFix WAF support, configuration questions, or to report JavaScript scripting issues, visit the Support section or contact us.