Configuration
Arcis works out of the box with secure defaults. Every option is tunable when you need it. You only opt out of protection, never in.
Passing config
All configuration goes through a single options object passed to arcis():
app.use(arcis({
sanitize: true,
sanitizeXss: true,
rateLimit: true,
rateLimitMax: 100,
rateLimitWindow: 60000, // ms
headers: true,
isDev: process.env.NODE_ENV === 'development',
}));
Sanitization options
| Option | Default | Description |
|---|---|---|
sanitize | true | Master switch. Disables all sanitization if false. |
sanitizeXss | true | Strip XSS patterns and HTML-encode output. |
sanitizeSql | true | Strip SQL injection patterns. |
sanitizeNoSql | true | Block 35 MongoDB operators ($gt, $where, etc.). |
sanitizePath | true | Strip path traversal (../, %2e%2e, etc.). |
sanitizeCmd | true | Strip command injection patterns. |
sanitizeSsti | true | Strip SSTI patterns (Jinja2, Twig, ERB, Pug). |
sanitizeXxe | true | Strip XXE patterns from XML input. |
maxInputSize | 1_000_000 | Max input size in bytes. Inputs over this are truncated. |
Rate limiting
| Option | Default | Description |
|---|---|---|
rateLimit | true | Enable rate limiting. |
rateLimitMax | 100 | Max requests per window. |
rateLimitWindow | 60000 | Window in milliseconds. |
rateLimitStore | memory | Storage: memory, Redis, or custom. |
rateLimitSkip | — | Function (req) => boolean. Return true to skip rate limiting. |
Using Redis
import { arcis, RedisStore } from '@arcis/node';
import { createClient } from 'redis';
const redis = createClient();
await redis.connect();
app.use(arcis({
rateLimitStore: new RedisStore({ client: redis }),
}));
Security headers
| Option | Default |
|---|---|
headers | true |
csp | strict policy (see below) |
frameOptions | DENY |
hstsMaxAge | 31536000 (1 year) |
hstsSubdomains | true |
referrerPolicy | strict-origin-when-cross-origin |
crossOriginOpenerPolicy | same-origin |
crossOriginResourcePolicy | same-origin |
crossOriginEmbedderPolicy | require-corp |
Default CSP:
default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline';
img-src 'self' data: https:; font-src 'self'; object-src 'none';
frame-ancestors 'none';
CORS
corsProtection() is opt-in middleware. Pass allowed origins explicitly:
import { corsProtection } from '@arcis/node';
app.use(corsProtection({
origin: ['https://yourapp.com', 'https://admin.yourapp.com'],
methods: ['GET', 'POST'],
credentials: true,
}));
CSRF
import { csrfProtection } from '@arcis/node';
app.use(csrfProtection({
cookieName: '__Host-csrf', // __Host- prefix requires Secure + Path=/
useHostPrefix: true,
skipCsrf: (req) => req.path.startsWith('/api/webhook/'),
}));
Development mode
In dev, show detailed error info. In production (default), scrub all sensitive info from errors.
app.use(arcis({
isDev: process.env.NODE_ENV === 'development',
}));