Skip to content

Latest commit

 

History

4 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

CI

arcis-example-nextjs

Minimal Next.js + Arcis app showing the two-layer pattern: Edge Middleware for rate-limit + bot, route handler for XSS / SQL / SSTI / etc. payload inspection.

What this is

A demo of how a real Next.js app wires Arcis. Next.js Edge Middleware can read URLs and headers but cannot consume request bodies (the stream would be unreadable downstream). So Arcis on Next.js works in two layers:

  1. Edge Middleware (middleware.ts) runs ahead of every request. Scope: rate-limit, bot detection, response security headers. Cheap, runs on every page + every API route.
  2. Route handler payload inspection (app/api/echo/route.ts) uses scanThreats from @arcis/node/sanitizers against the parsed body and query. Catches XSS, SQL, NoSQL, path, command, SSTI, XXE, prototype, LDAP, XPath, header-injection payloads. Runs only on API routes that handle untrusted input.

Files in this repo:

Total runtime dependencies: @arcis/node + next + react. Nothing else.

What this adapter does and does not do

Protection Where it runs Built in?
Rate limiting (per-IP, in-memory) arcisMiddleware in middleware.ts yes
Bot detection arcisMiddleware in middleware.ts yes
Response security headers (CSP, HSTS, X-Frame-Options, etc.) arcisMiddleware via arcisProtect wrap on outbound yes
Input sanitization (XSS, SQL, NoSQL, path, command, SSTI, XXE, prototype, LDAP, XPath, header injection) scanThreats inside app/api/echo/route.ts yes, opt-in per-route
CSRF protection not in this demo (opt-in) use csrf() from @arcis/node/middleware
CORS not in this demo (opt-in) configure in next.config.js or per-route
Secure cookies not in this demo (opt-in) use secureCookies() from @arcis/node/middleware
URL / redirect / file-upload validation not in this demo (opt-in) validateUrl, validateRedirect, validateFile from @arcis/node/validation
Error-leakage scrubbing not in this demo (opt-in) use errorHandler() from @arcis/node/middleware

The 8-payload attack.js exercises both layers: rate-limit and bot get caught by Layer 1; XSS/SQL/SSTI/etc. payloads get caught by Layer 2. Other concerns (CSRF, CORS, validation) are deliberate opt-ins per the same pattern as the other Arcis examples.

Run it

npm install
npm run dev        # listens on http://localhost:3000
npm run attack     # in another shell, fires the demo payloads

Expected output:

Arcis attack demo against http://localhost:3000
----------------------------------------------------------------
OK     safe     safe input: 200 (passed through, as expected)
BLOCK  xss      <script> in query: 403 (Arcis denied, as expected)
BLOCK  xss      event handler: 403 (Arcis denied, as expected)
BLOCK  sql      '; DROP TABLE users; --: 403 (Arcis denied, as expected)
BLOCK  nosql    { $gt: "" } operator: 403 (Arcis denied, as expected)
BLOCK  path     ../../etc/passwd: 403 (Arcis denied, as expected)
BLOCK  command  ; rm -rf /: 403 (Arcis denied, as expected)
BLOCK  ssti     Jinja2 {{7*7}}: 403 (Arcis denied, as expected)
BLOCK  xxe      DOCTYPE ENTITY: 403 (Arcis denied, as expected)
----------------------------------------------------------------
8 attacks blocked, 1 safe call passed, 0 unexpected

How it works

Layer 1 — Edge Middleware

middleware.ts:

import { arcisMiddleware } from '@arcis/node/nextjs';
import { NextResponse } from 'next/server';

const arcis = arcisMiddleware({
  rateLimit: { max: 100, windowMs: 60_000 },
  bot: true,
});

export default async function middleware(request: Request) {
  const blocked = await arcis(request);
  if (blocked) return blocked;
  return NextResponse.next();
}

export const config = {
  matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],
};

arcisMiddleware returns a Response (429 for rate-limit, 403 for bot) when a request hits the pipeline, or undefined when it should fall through. Edge Middleware is block-or-pass by design, so unlike the Express adapter, there is no block toggle. Body inspection is not in this layer because Edge Middleware cannot consume the request body without making it unreadable in the route handler.

Layer 2 — route-handler payload inspection

app/api/echo/route.ts:

import { scanThreats } from '@arcis/node/sanitizers';

export async function POST(request: Request) {
  const body = await request.json().catch(() => ({}));
  const url = new URL(request.url);
  const query = Object.fromEntries(url.searchParams);

  const hit = scanThreats(body) ?? scanThreats(query);
  if (hit) {
    return Response.json(
      { error: 'request blocked by Arcis', vector: hit.vector, rule: hit.rule },
      { status: 403 },
    );
  }

  return Response.json({ received: body });
}

scanThreats is the same function the Express adapter calls internally when you set arcis({ block: true }). It returns a ThreatHit | null describing the first vector / rule that fired, or null if the input is clean. Apply it on any API route that accepts untrusted input.

Production rollout note

Use Layer 1 globally (it is cheap and catches the bots that scrape your whole app). Add Layer 2 to API routes that take untrusted input. For routes that only echo back internal state, Layer 2 is unnecessary. For deeper coverage, the main Arcis docs cover CSRF, CORS, cookies, validation, and error scrubbing as separate opt-in middleware.

Sister examples

Framework Repo
Express arcis-example-express
FastAPI arcis-example-fastapi
Gin (Go) arcis-example-gin
Bun + Hono arcis-example-bun
NestJS arcis-example-nestjs

License

MIT. See LICENSE.

About

Minimal Next.js + Arcis demo. One middleware.ts line, 20+ attack vectors blocked. Includes an 8-payload attack script.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages