Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.lintliot.com/llms.txt

Use this file to discover all available pages before exploring further.

LintLiot’s Request Shield runs a full Web Application Firewall on every incoming request before your application code executes. You don’t configure rules, write policies, or maintain anything — protection is active the moment you call lintliot.protect(). The WAF, bot detection, IP reputation checks, and session protection all run in-process with under 1ms overhead.

How the WAF works

The WAF inspects four parts of every request: the URL path, query parameters, request body, and headers. Before matching, LintLiot normalizes each input — URL-decoding it twice, stripping null bytes, decoding HTML entities — so that encoding-based bypass attempts (like %2527 double-encoded SQL payloads) are caught alongside standard attack strings. Each pattern carries a numeric risk score. A single critical-severity hit (score ≥ 8) immediately blocks the request. Lower-severity patterns accumulate: if multiple medium-risk signals appear in the same request, the combined score can also trigger a block. This weighted approach reduces false negatives without requiring you to tune thresholds.

Attack categories covered

The built-in pattern library covers over 70 patterns across 15 attack categories:
  • SQL injection — UNION SELECT, OR-based bypass, stacked queries, time-based blind, error-based, information_schema enumeration
  • NoSQL injection — MongoDB $where, $gt, $regex, and operator injection patterns
  • Command injection — shell metacharacters, subshell execution ($(cmd)), backtick execution, process substitution
  • LDAP injection — filter injection and dangerous LDAP characters
  • Log4Shell${jndi:...} and related Log4j exploitation patterns
  • XSS<script> tags, event handlers (onerror=, onload=), javascript: URIs, data:text/html payloads, SVG and iframe injection, DOM-based template injection
  • Open redirect — query parameters that redirect to external domains (redirect_uri, next, goto, returnUrl)
  • Prototype pollution__proto__, constructor.prototype manipulation
  • SSRF — localhost probes (127.0.0.1, 0x7f000001, 0177.0.0.1), cloud metadata endpoints (169.254.169.254, metadata.google.internal), RFC-1918 private ranges, file:// URIs
  • SSTI — Python/Jinja2 class traversal, Twig/Smarty execution, Java EL (${T(Runtime)...})
  • XXE — external entity declarations, DOCTYPE with entity references, XML bomb (Billion Laughs)
  • Path traversal../ sequences, Windows ..\\, encoded variants, access to /etc/passwd, .ssh/, .aws/credentials
  • HTTP request smuggling — conflicting Transfer-Encoding and Content-Length headers
  • Java deserializationrO0AB magic bytes, PHP serialized objects
  • Spring Expression Language injection${T(java.lang.Runtime)...} patterns
  • GraphQL abuse — introspection queries and field suggestion bypasses
  • Scanner signatures — SQLMap, Nikto, Nuclei, and other automated attack tool fingerprints

Enabling the WAF

The WAF is on by default when you call lintliot.protect(). No additional configuration is needed.
// middleware.ts
import { withLintliot } from '@lintliot/sdk/next'

export default withLintliot({ apiKey: process.env.LINTLIOT_API_KEY })
export const config = { matcher: ['/((?!_next|static|favicon).*)'] }
To run the WAF in monitor-only mode (log but don’t block), pass waf: 'monitor':
app.use(lintliot.protect({ waf: 'monitor' }))

Bot detection

LintLiot scores every request for bot behavior on a 0–100 scale rather than making a binary pass/fail decision. A score of 80 or above triggers a block; scores between 30 and 79 are monitored and logged.

Fingerprinting signals

The bot detector evaluates 12 signals per request:
SignalHow it’s used
User-Agent stringMatched against known attack tools (SQLMap, Nikto, Gobuster, etc.) and automation frameworks (Puppeteer, Playwright, HeadlessChrome)
Missing required headersLegitimate browsers always send Accept and Accept-Encoding
Missing optional headersAbsence of Accept-Language, DNT, and Sec-Fetch-* headers raises the score
Headless browser fingerprintSec-Fetch-Mode present without Sec-Fetch-User indicates headless Chrome
Request timingRequests arriving less than 8ms apart from the same IP indicate automation
Honeypot pathsRequests to /wp-admin, /.env, /.git/config, /phpinfo.php, and other trap paths are blocked immediately
Accumulated IP historyPrior suspicious activity from the same IP carries forward (with 30% decay)
UA lengthUser-Agents shorter than 8 characters are treated as highly suspicious
Known attack tool UAssqlmap, Nikto, w3af, OWASP ZAP, Acunetix, and others trigger an instant block
Scored automation UAsHeadlessChrome (60), Puppeteer (60), PhantomJS (80), HTTrack (70), Scrapy (55)
SEO crawler whitelistGooglebot, Bingbot, and 20+ legitimate crawlers are always allowed through
API client monitoringcurl, Python requests, Go HTTP client are monitored but not blocked
Trusted SEO crawlers (Googlebot, Bingbot, Apple Bot, and others) are always allowed regardless of other signals. Bot detection never harms your search engine indexing.

Account takeover protection

Brute force blocking

When the same IP address fails login 10 times within a 5-minute window, LintLiot blocks that IP with a progressive penalty: the first block lasts one window (5 min), the second lasts two windows, the third lasts four, and subsequent violations lock the IP for 8x the base window. Report excessive failures to lintliot.anomaly.recordAuthFailure():
// In your login route handler
const result = await lintliot.anomaly.recordAuthFailure(req, targetUserId)

if (result.detected) {
  // result.type === 'brute_force' or 'credential_stuffing'
  return res.status(429).json({ error: 'Too many failed attempts' })
}

Credential stuffing detection

If 5 or more unique IP addresses target the same user account with failed login attempts within a 10-minute window, LintLiot flags it as credential stuffing. All source IPs are blocked, the account is flagged, and an alert fires to your dashboard. You can adjust the threshold:
const lintliot = createLintliot({
  apiKey: process.env.LINTLIOT_API_KEY,
  anomalyThresholds: {
    stuffingIPs: 3,         // trigger on 3 unique IPs (default: 5)
    bruteForceFailures: 5,  // trigger on 5 failures (default: 10)
  },
})

Session fingerprinting and hijack detection

Every session is fingerprinted at creation from four signals: the IP /24 subnet (35% weight), browser User-Agent family (25%), Accept-Language header (15%), and platform/OS derived from the User-Agent (25%). If a session token is later presented with a fingerprint that matches less than 40% of the original signals, LintLiot treats it as a potential session hijack. The legitimate user is challenged with a verification step; the attacker receives a 403 and the event is logged as auth.session_hijack_attempt.

Impossible travel detection

LintLiot detects when the same user account authenticates from two geographic locations faster than is physically possible. It uses the haversine formula to calculate the great-circle distance between the two login coordinates, then compares the implied speed against the maximum speed of commercial aviation (900 km/h).
distance_km = haversine(lat1, lon1, lat2, lon2)
elapsed_hours = (login2_timestamp - login1_timestamp) / 3_600_000
implied_speed_kmh = distance_km / elapsed_hours

if implied_speed_kmh > 900: impossible_travel = true
When impossible travel is detected:
  • The second session is invalidated automatically
  • An auth.impossible_travel event is logged at critical severity
  • A push notification fires to the dashboard immediately
  • The user receives a “Login from new location” email (if configured)
Impossible travel detection requires geo-enriched requests. LintLiot enriches IP addresses automatically — no additional configuration is needed.

Route sensitivity rules

LintLiot automatically applies elevated protection to sensitive route patterns. Eight patterns are enforced by default:
Route patternProtection applied
/admin*, /api/admin*Blocked if unauthenticated; all access audit-logged
/delete*, /remove*, /destroy*All access audit-logged
/export*, /download*, /report*All access audit-logged; egress monitoring active
/payment*, /billing*, /checkout*All access audit-logged with full payload hash
/settings/delete*Blocked if unauthenticated; all access audit-logged
/bulk*, /batch*Record count monitoring; exfiltration threshold applied
You can add custom sensitivity rules in your SDK configuration:
const lintliot = createLintliot({
  apiKey: process.env.LINTLIOT_API_KEY,
  routeSensitivity: {
    '/api/internal*': 'critical',   // block unauthenticated
    '/reports/financial*': 'high',  // audit all access
  },
})
Or define structured rules with full control:
app.use(lintliot.protect({
  routeSensitivity: [
    {
      pattern: /\/api\/admin/,
      level: 'critical',
      requireAuth: true,
      auditAll: true,
    },
    {
      pattern: /\/export\//,
      level: 'high',
      auditAll: true,
    },
  ],
}))

Custom IP rules

You can block or allow specific IP addresses and ranges from the LintLiot dashboard under Shield → IP Rules. Changes propagate to your running application via Edge Config within 60 seconds — no redeployment required.
IP rules you configure in the dashboard override WAF decisions. An IP on your allowlist will never be blocked by WAF patterns, even if it sends malicious-looking payloads.