Skip to main content

Built-in Protection

Rule Engine JS has security features enabled by default:

Prototype Protection

Blocks proto and constructor access

Function Blocking

Functions cannot be accessed via paths

Depth Limits

Prevents infinite recursion attacks

Type Validation

Validates operator arguments

Prototype Pollution

What It Is

Malicious path access that modifies object prototypes.
// ❌ DANGEROUS - Don't allow this
const maliciousPath = '__proto__.isAdmin';
const maliciousData = {
  '__proto__': { isAdmin: true }
};

How We Block It

// ✅ BLOCKED automatically
engine.resolvePath(data, '__proto__');
// Returns: undefined (blocked)

engine.resolvePath(data, 'constructor');
// Returns: undefined (blocked)

engine.resolvePath(data, '__proto__.polluted');
// Returns: undefined (blocked)

Configuration

const engine = createRuleEngine({
  allowPrototypeAccess: false  // Default: false (KEEP IT!)
});
Never set allowPrototypeAccess: true in production! It opens critical security vulnerabilities.

Input Validation

Always validate user input before using in rules.

Bad vs Good

  • ❌ Bad - No Validation
  • ✅ Good - Validated
// Direct user input (DANGEROUS!)
app.post('/check', (req, res) => {
  const rule = req.body.rule;  // Unvalidated!
  const result = engine.evaluateExpr(rule, data);
  res.json(result);
});

Whitelist Operators

Only allow specific operators in user-defined rules.
const SAFE_OPERATORS = [
  'eq', 'neq', 'gt', 'gte', 'lt', 'lte',
  'and', 'or', 'not',
  'in', 'notIn',
  'contains', 'startsWith', 'endsWith'
];

function validateRule(rule) {
  const operators = extractOperators(rule);
  return operators.every(op => SAFE_OPERATORS.includes(op));
}

function extractOperators(rule) {
  if (typeof rule !== 'object') return [];

  const ops = Object.keys(rule);
  const nested = Object.values(rule)
    .filter(v => typeof v === 'object')
    .flatMap(extractOperators);

  return [...ops, ...nested];
}

// Usage
if (!validateRule(userRule)) {
  throw new Error('Rule contains disallowed operators');
}

Whitelist Paths

Restrict which data paths can be accessed.
const ALLOWED_PATHS = [
  'user.name',
  'user.age',
  'user.email',
  'order.total',
  'order.status'
];

function validatePath(path) {
  return ALLOWED_PATHS.includes(path);
}

function validateRulePaths(rule) {
  const paths = extractPaths(rule);
  return paths.every(validatePath);
}

function extractPaths(rule) {
  if (Array.isArray(rule)) {
    return rule
      .filter(item => typeof item === 'string')
      .filter(item => item.includes('.'));
  }

  return Object.values(rule).flatMap(extractPaths);
}

Rate Limiting

Prevent DoS via excessive evaluations.
const rateLimit = require('express-rate-limit');

const ruleLimiter = rateLimit({
  windowMs: 60 * 1000,  // 1 minute
  max: 100,              // 100 requests per minute
  message: 'Too many rule evaluations'
});

app.post('/evaluate', ruleLimiter, (req, res) => {
  const result = engine.evaluateExpr(rule, data);
  res.json(result);
});

Depth Limits

Prevent deeply nested rules (DoS).
const engine = createRuleEngine({
  maxDepth: 10,        // Default: 10
  maxOperators: 100    // Default: 100
});

// This will fail
const deepRule = {
  and: [{ and: [{ and: [/* ... 20 levels deep ... */] }] }]
};

engine.evaluateExpr(deepRule, data);
// Error: "Rule exceeds maximum depth of 10"

Sensitive Data

Don’t expose sensitive data in error messages.
  • ❌ Bad
  • ✅ Good
// Exposes sensitive data
try {
  engine.evaluateExpr(rule, sensitiveData);
} catch (error) {
  res.json({ error: error.message, data: sensitiveData });
}

Regex Safety

Prevent ReDoS (Regular Expression Denial of Service).
// ❌ Dangerous regex patterns
const unsafe = [
  '(a+)+b',           // Catastrophic backtracking
  '(a|a)*b',
  '(a*)*b'
];

// ✅ Safe regex patterns
const safe = [
  '^[a-z]+@[a-z]+\\.[a-z]{2,}$',  // Simple patterns
  '^\\d{3}-\\d{3}-\\d{4}$'
];

// Validate regex before use
function isSafeRegex(pattern) {
  // Check for dangerous patterns
  const dangerous = [/\(\w\+\)\+/, /\(\w\*\)\*/];
  return !dangerous.some(d => d.test(pattern));
}

Complete Security Checklist

allowPrototypeAccess: false (default)
Never override in production
Validate rule structure
Whitelist operators
Whitelist paths
Sanitize user input
maxDepth: 10 (or lower)
maxOperators: 100 (or lower)
Limit requests per IP
Limit evaluations per user
Monitor for abuse
Generic error messages
Log errors privately
Don't expose sensitive data
Block dangerous patterns
Timeout regex matching
Test patterns before use

Secure Configuration

const engine = createRuleEngine({
  // Security
  allowPrototypeAccess: false,  // Never true!
  strict: true,                 // Prevent type coercion bugs
  maxDepth: 10,                 // Limit nesting
  maxOperators: 100,            // Limit complexity

  // Performance
  enableCache: true,
  maxCacheSize: 1000
});