npm.io
0.1.0 • Published 2d ago

llm-firewall-js

Licence
MIT
Version
0.1.0
Deps
0
Size
40 kB
Vulns
0
Weekly
0

llm-firewall-js

A lightweight, framework-agnostic JavaScript firewall for protecting LLM API routes from prompt attacks, secret leakage, spam abuse, and runaway usage costs.

npm install llm-firewall-js
import { createLLMFirewall } from "llm-firewall-js"

const firewall = createLLMFirewall()

What Is LLM Firewall?

llm-firewall-js is a server-side guardrail layer for applications that use large language models.

It sits between your frontend and your LLM provider:

Frontend -> Your API Route -> llm-firewall-js -> LLM Provider

It does not call OpenAI, Anthropic, Gemini, Groq, or any other provider for you. Instead, it protects your own API route before a request reaches any LLM and inspects the response before it returns to users.

The package is designed for developers shipping public AI features, AI widgets, prototypes, portfolios, SaaS assistants, internal tools, and no-code/low-code AI integrations.

Features

  • Provider-agnostic: works with OpenAI, Anthropic, Gemini, Groq, local models, or any other LLM.
  • Framework-agnostic: works with Next.js, Express, Node.js, Vercel API Routes, and generic backend APIs.
  • Prompt attack detection for common jailbreak and prompt-extraction attempts.
  • Secret extraction blocking for API keys, environment variables, hidden prompts, and owner tokens.
  • Output leak detection before model responses reach users.
  • Input size limits to reduce high-token abuse.
  • Per-IP burst rate limiting.
  • Per-IP daily request limits.
  • Per-IP daily token-estimate limits.
  • Repeated payload detection for spam loops.
  • CORS/origin handling with exact domains, wildcards, or *.
  • Owner bypass token for private testing.
  • Kill switch for quickly disabling all AI calls.
  • Optional Upstash Redis support for persistent serverless limits.
  • Zero runtime dependencies.

Installation

npm install llm-firewall-js

Requirements:

  • Node.js >=18
  • Server-side runtime

Do not use this package directly in public browser code. It is built for API routes and backend servers.

Quick Start

import { createLLMFirewall } from "llm-firewall-js"

const firewall = createLLMFirewall()

export default async function handler(req, res) {
  firewall.setCors(req, res)

  if (req.method === "OPTIONS") {
    res.status(204).end()
    return
  }

  const body = typeof req.body === "string" ? JSON.parse(req.body) : req.body || {}

  const inputCheck = await firewall.inspectInput({
    request: req,
    body,
    text: body.question || body.prompt || "",
  })

  if (!inputCheck.ok) {
    firewall.reject(res, inputCheck)
    return
  }

  const modelData = await callYourLLMProvider(body)

  const outputCheck = firewall.inspectOutput(modelData)
  if (!outputCheck.ok) {
    firewall.reject(res, outputCheck)
    return
  }

  res.status(200).json(outputCheck.data)
}

API Reference

createLLMFirewall(options?)

Creates a firewall instance.

const firewall = createLLMFirewall({
  allowedOrigins: ["https://example.com"],
  maxInputChars: 4000,
  rateLimitMax: 8,
})

Returns:

{
  config,
  setCors,
  inspectInput,
  inspectOutput,
  reject,
  ownerBypass,
  getClientIp
}
firewall.setCors(request, response)

Sets CORS headers on Node, Express, Vercel, or Fetch Response objects.

Use this for:

  • OPTIONS preflight requests
  • normal API responses
  • blocked firewall responses
firewall.inspectInput({ request, body, text })

Inspects user input before it reaches your LLM provider.

const inputCheck = await firewall.inspectInput({
  request: req,
  body: req.body,
  text: req.body.question,
})

Returns:

{ ok: true }

or:

{
  ok: false,
  status: 400,
  reason: "This request looks like a prompt attack or secret-extraction attempt."
}
firewall.inspectOutput(data)

Inspects the model output before returning it to users.

const outputCheck = firewall.inspectOutput(modelData)

Returns:

{ ok: true, data: modelData }

or:

{
  ok: false,
  status: 500,
  reason: "The model response was blocked because it looked like it could expose hidden instructions or secrets."
}
firewall.reject(response, inspection)

Sends or creates a JSON blocked response.

For Express/Vercel:

firewall.reject(res, inputCheck)

For Fetch/Next.js route handlers:

const blocked = firewall.reject(null, inputCheck)
firewall.setCors(request, blocked)
return blocked

Blocked response shape:

{
  "error": "Request blocked by LLM firewall.",
  "firewall": {
    "blocked": true,
    "reason": "blocked"
  }
}
firewall.ownerBypass(request)

Returns true when a request includes the configured owner bypass token.

firewall.getClientIp(request)

Reads client IP from common proxy and serverless headers.

Configuration

All options can be passed directly:

const firewall = createLLMFirewall({
  enabled: true,
  killSwitch: false,
  ownerBypassToken: process.env.OWNER_BYPASS_TOKEN,
  allowedOrigins: ["https://example.com", "https://*.example.com"],
  maxInputChars: 8000,
  maxOutputTokens: 650,
  rateLimitWindowMs: 60_000,
  rateLimitMax: 12,
  dailyRequestLimit: 250,
  dailyTokenEstimateLimit: 180_000,
  repeatedPayloadWindowMs: 10 * 60_000,
  repeatedPayloadMax: 8,
})
Custom Text Extraction

By default, the firewall checks common body fields:

input, prompt, question, message, messages, jobDescription, text

For custom payloads:

const firewall = createLLMFirewall({
  textExtractor(body) {
    return [
      body.userMessage,
      body.context,
      body.uploadedText,
    ].join("\n")
  },
})
Custom Rules
const firewall = createLLMFirewall({
  suspiciousPatterns: [
    /ignore\s+previous\s+instructions/i,
    /reveal\s+(the\s+)?system\s+prompt/i,
    /my-private-internal-term/i,
  ],
  outputLeakPatterns: [
    /OPENAI_API_KEY/i,
    /DATABASE_URL/i,
    /\b(sk|sk-proj)-[A-Za-z0-9_-]{20,}\b/i,
  ],
})

Environment Variables

Every option can also be configured through environment variables.

Variable Default Purpose
LLM_FIREWALL_ENABLED true Turn the firewall on/off. Set false to disable.
AI_KILL_SWITCH false Stop all AI calls immediately.
OWNER_BYPASS_TOKEN empty Private token for owner-only testing.
ALLOWED_ORIGINS * Comma-separated origin allowlist. Supports wildcards.
MAX_INPUT_CHARS 8000 Maximum extracted input length.
MAX_OUTPUT_TOKENS 650 Estimated output token allowance for usage limits.
RATE_LIMIT_WINDOW_MS 60000 Burst rate-limit window.
RATE_LIMIT_MAX 12 Requests allowed per window per IP.
DAILY_REQUEST_LIMIT 250 Daily request cap per IP.
DAILY_TOKEN_ESTIMATE_LIMIT 180000 Daily estimated token cap per IP.
REPEATED_PAYLOAD_WINDOW_MS 600000 Window for repeated payload detection.
REPEATED_PAYLOAD_MAX 8 Repeated identical payloads allowed per window.
UPSTASH_REDIS_REST_URL empty Optional Upstash Redis REST URL.
UPSTASH_REDIS_REST_TOKEN empty Optional Upstash Redis REST token.

Recommended starter settings for public demos:

MAX_INPUT_CHARS=4000
MAX_OUTPUT_TOKENS=500
RATE_LIMIT_MAX=8
DAILY_REQUEST_LIMIT=120
DAILY_TOKEN_ESTIMATE_LIMIT=90000

Examples

Examples are included in the examples folder.

Express
import express from "express"
import { createLLMFirewall } from "llm-firewall-js"

const app = express()
const firewall = createLLMFirewall()

app.use(express.json({ limit: "32kb" }))

app.post("/api/chat", async (req, res) => {
  firewall.setCors(req, res)

  const inputCheck = await firewall.inspectInput({
    request: req,
    body: req.body,
    text: req.body.question || req.body.prompt || "",
  })

  if (!inputCheck.ok) {
    firewall.reject(res, inputCheck)
    return
  }

  const modelData = await callYourLLMProvider(req.body)
  const outputCheck = firewall.inspectOutput(modelData)

  if (!outputCheck.ok) {
    firewall.reject(res, outputCheck)
    return
  }

  res.json(outputCheck.data)
})
Next.js App Router

Use in app/api/chat/route.js.

import { createLLMFirewall } from "llm-firewall-js"

const firewall = createLLMFirewall()

export async function POST(request) {
  const body = await request.json()

  const inputCheck = await firewall.inspectInput({
    request,
    body,
    text: body.question || body.prompt || "",
  })

  if (!inputCheck.ok) {
    const blocked = firewall.reject(null, inputCheck)
    firewall.setCors(request, blocked)
    return blocked
  }

  const modelData = await callYourLLMProvider(body)
  const outputCheck = firewall.inspectOutput(modelData)

  const response = Response.json(outputCheck.ok ? outputCheck.data : { error: outputCheck.reason })
  firewall.setCors(request, response)
  return response
}
Vercel API Route

See examples/vercel-api/chat.js.

Generic Node

See examples/generic-node/server.js.

Security Features

Input Protection

Blocks common attempts to:

  • ignore previous instructions
  • reveal system prompts
  • show hidden instructions
  • leak API keys or secrets
  • access environment variables
  • jailbreak or enable developer mode
  • encode hidden instructions
  • spam repeated payloads
Output Protection

Inspects model responses for:

  • system prompt leakage
  • developer message leakage
  • hidden instruction leakage
  • common secret environment variable names
  • OpenAI-style secret key strings
Cost Protection

Controls:

  • maximum input size
  • per-IP burst rate
  • per-IP daily request count
  • per-IP daily token estimate
  • repeated identical payloads
Operational Controls

Supports:

  • kill switch
  • owner bypass
  • CORS helper
  • environment-based configuration
  • optional persistent Redis limits

Supported Frameworks

  • Express
  • Next.js App Router
  • Vercel API Routes
  • Node.js HTTP servers
  • Generic backend APIs
  • Serverless functions that support JavaScript ESM

Supported LLM Providers

llm-firewall-js is provider-agnostic. It can be used before any server-side provider call, including:

  • OpenAI
  • Anthropic
  • Google Gemini
  • Groq
  • Mistral
  • Cohere
  • Azure OpenAI
  • local/self-hosted models
  • any custom LLM API

Limitations

This package is a practical first layer of defense. It does not make AI apps unhackable.

Use it alongside:

  • provider billing alerts
  • provider spend limits
  • authentication for private products
  • product-level user quotas
  • request logging and monitoring
  • abuse detection
  • secret scanning
  • regular key rotation
  • human review for high-risk workflows

Roadmap

  • TypeScript declarations.
  • First-class middleware helpers.
  • Structured logging hooks.
  • Custom storage adapter interface.
  • Per-user limits for authenticated apps.
  • Optional rule packs for stricter enterprise use cases.
  • Test suite with fixture-based prompt attack cases.

Created By

Created by Darshan Sawant

Disclaimer

This project is provided for educational and experimental use. Review, test, and adapt it before using it in production. It reduces common LLM API risks, but it does not guarantee complete protection against abuse, prompt injection, data leakage, or unexpected billing.

License

MIT

Keywords