Repository: rules-machine Date: April 6, 2026 Version: 1.6.0 Auditor: AI Code Review Agent
This is a mature, well-structured JSON rules engine library for Node.js and browsers. The codebase demonstrates solid engineering practices with comprehensive test coverage, TypeScript typing, and multi-format build output. However, several areas require attention for modern standards and security hardening.
Overall Health: Good (7/10)
| Category | Score | Status |
|---|---|---|
| Code Quality | 8/10 | Good |
| Security | 6/10 | Needs Work |
| Testing | 8/10 | Good |
| Documentation | 9/10 | Excellent |
| Dependencies | 5/10 | Critical |
| CI/CD | 4/10 | Needs Work |
| TypeScript | 7/10 | Good |
Category: Security / Maintenance Severity: Critical
Description: Multiple core dependencies are significantly outdated with known security vulnerabilities:
| Package | Current | Latest | Risk |
|---|---|---|---|
| jest | 26.x.x | 30.x | High |
| ts-jest | 26.x.x | 29.x | High |
| typescript | 4.8.4 | 5.8.x | Medium |
| eslint | 8.26.0 | 9.x | Medium |
| lodash | 4.17.21 | 4.17.21+ | Known CVEs |
| debug | 4.3.3 | 4.4.x | Low |
Impact:
- Known security vulnerabilities in test dependencies could affect CI/CD pipeline
- Missing security patches and performance improvements
- Compatibility issues with modern Node.js versions
Remediation:
# Update package.json devDependencies
"devDependencies": {
"jest": "^29.7.0",
"ts-jest": "^29.2.0",
"typescript": "^5.8.0",
"eslint": "^9.0.0",
"@typescript-eslint/eslint-plugin": "^8.0.0",
"@typescript-eslint/parser": "^8.0.0"
}Category: DevOps / Security Severity: Critical
Location: .github/workflows/test.yml
Description: The GitHub Actions workflow uses severely outdated action versions:
actions/checkout@v2(current: v4)actions/setup-node@v2(current: v4)- Only tests Node.js 14.x (EOL since April 2023)
Impact:
- Security vulnerabilities in outdated action runners
- No testing on modern Node.js LTS versions (18, 20, 22)
- Potential CI/CD supply chain attacks
Remediation:
name: test
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18.x, 20.x, 22.x]
steps:
- uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'yarn'
- run: yarn install --frozen-lockfile
- run: yarn lint
- run: yarn testCategory: Security Severity: High
Location: src/index.ts:479-499 (checkInvalidKeys)
Description: While checkInvalidKeys blocks dangerous keys like __proto__, constructor, and prototype, the protection is incomplete:
- Only validates top-level input keys
- Does not protect against nested prototype pollution via rule expressions
- The
parser.expressionToValue()could be exploited with crafted expressions
Evidence:
function checkInvalidKeys<TInput extends object>(data: TInput) {
const dangerousKeys = [
'__proto__', 'prototype', 'constructor', 'toString',
'valueOf', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable',
];
// Only checks Object.keys(data) - shallow check only
const unsafeKeys = Object.keys(data).filter((key) =>
dangerousKeys.includes(key),
);Impact:
- Potential prototype pollution via nested objects in rules
- Could lead to remote code execution in server environments
- Bypass of security controls
Remediation:
- Implement deep key validation recursively
- Use
Object.create(null)for all object creation - Add runtime checks in
evaluateRuleand expression parser - Consider using a library like
deepmergewith prototype pollution protection
Category: Security Severity: High
Location: src/index.ts:88-118, src/expression-language/index.ts
Description: The codebase uses expressionparser package for parsing expressions. While it claims to avoid eval, the expression parser still executes arbitrary code through function calls:
- User-provided strings are parsed and executed as expressions
- The
THROWfunction allows arbitrary error messages (potential DoS) - No sandboxing or resource limits on expression execution
Evidence:
const parser = init(ruleExpressionLanguage, (term: string) => {
// term comes from user input via rules
const result = extractValueOrLiteral(input, term, ...) ?? get(input, term, ...)
return result;
});Impact:
- Denial of Service via complex expressions (ReDoS-like attacks)
- Information disclosure through error messages
- Potential code execution if expressionparser has vulnerabilities
Remediation:
- Add expression complexity limits (max depth, max operations)
- Implement timeout mechanisms for rule execution
- Whitelist allowed functions explicitly
- Add input sanitization before parsing
Category: Security Severity: High
Location: src/index.ts:120-330 (handleRule)
Description: The handleRule function trusts the structure of incoming rules without comprehensive validation:
- No maximum recursion depth for nested rules
- No validation of rule object structure before processing
- Type coercion happens implicitly in many places
Impact:
- Stack overflow via deeply nested rules
- Unexpected behavior from malformed rule objects
- Potential for rule injection attacks
Remediation:
function validateRule(rule: unknown, depth = 0): asserts rule is Rule {
const MAX_DEPTH = 50;
if (depth > MAX_DEPTH)
throw new UserError('Maximum rule nesting depth exceeded');
if (typeof rule === 'string') return;
if (Array.isArray(rule)) {
rule.forEach((r) => validateRule(r, depth + 1));
return;
}
if (typeof rule !== 'object' || rule === null) {
throw new UserError('Invalid rule type');
}
// Validate specific rule types...
}Category: Code Quality Severity: Medium
Location: src/utils/errors.ts
Description: The UserError class suppresses stack traces completely:
get stack() {
return ''; // Always returns empty string
}Impact:
- Difficult debugging in production
- No error tracing for support teams
- Loss of valuable debugging context
Remediation:
export class UserError extends Error {
name = 'UserError';
isUserError = true;
constructor(message: string, options?: { cause?: unknown }) {
super(message, options);
}
}
// In error handling:
catch (e) {
if (e instanceof UserError) {
// Log without stack
logger.warn(e.message);
} else {
// Log full stack for unexpected errors
logger.error('Unexpected error:', e);
}
}Category: Code Quality Severity: Medium
Location: src/index.ts, tsconfig.json
Description: While strict: true is enabled, there are multiple @ts-expect-error comments and any types:
// src/index.ts:307
// @ts-expect-error
set(input, rule.set, arrayResult);
// src/index.ts:361
// @ts-expect-error: todo: fix this, add proper type for Result
results.runTime = performance.now() - startTime;Impact:
- Reduced type safety guarantees
- Potential runtime errors not caught at compile time
- Technical debt accumulation
Remediation:
- Remove all
@ts-expect-errorcomments with proper fixes - Replace
anytypes with proper type definitions - Enable
noImplicitAnyif not already enabled
Category: Security Severity: Medium
Location: src/index.ts:479-499
Description: The checkInvalidKeys function only runs once at the beginning of rule execution. The special keys $item, $index, $array are protected, but they're injected during array operations:
Object.assign(input, { $item: item, $index: index, $array: data });Impact:
- If rules could be injected mid-execution, reserved keys could be exploited
- No validation that input doesn't shadow these keys before array operations
Remediation:
- Check for reserved keys before each array operation
- Use a separate scope object for iteration variables
- Document reserved keys prominently in README
Category: Performance Severity: Low
Location: src/expression-language/index.ts
Description: Functions like DATEISO, DATE are called repeatedly without caching:
DATEISO: (arg) => {
const dateArg = arg();
// Creates new Date every time, even for same input
return new Date(dateParser(dateArg)).toISOString();
};Impact:
- Unnecessary object allocation for repeated calls
- Performance degradation in high-throughput scenarios
Remediation:
- Add LRU cache for date parsing results
- Consider memoizing pure function results
Category: Documentation Severity: Low
Description: While the README is comprehensive, several gaps exist:
- No security considerations section
- Missing performance benchmarks
- No guidance on rule complexity limits
- No examples of error handling patterns
Remediation: Add sections to README.md:
- Security Considerations
- Performance Guidelines
- Error Handling Best Practices
- Rule Complexity Recommendations
-
No eval/Function usage: The codebase explicitly avoids
eval()andFunction()constructors, using a proper expression parser instead. -
Comprehensive test coverage: Good test coverage with snapshots for trace output verification.
-
Clean architecture: Well-organized separation of concerns between expression language, utils, and main logic.
-
Multi-format builds: Supports CJS, ESM, and IIFE formats for broad compatibility.
-
Good type definitions: TypeScript types are exported and generally well-defined.
-
Trace functionality: Built-in tracing for debugging rule execution flow.
-
Reserved key protection: Attempts to prevent prototype pollution via dangerous key blocking.
- Functional approach: Heavy use of functional patterns makes rules predictable and testable.
- Immutable-by-default: Rules don't mutate input (except for explicit assignments).
- Composable: Rules can be nested and combined flexibly.
- Error handling strategy: Inconsistent error handling between UserError and system errors.
- Input validation: Validation happens at boundaries but not consistently throughout.
- Resource limits: No built-in limits on rule complexity or execution time.
-
Update GitHub Actions
- Update to
actions/checkout@v4andactions/setup-node@v4 - Add Node.js 18, 20, 22 to test matrix
- Update to
-
Update core dependencies
- Jest, ts-jest to latest versions
- TypeScript to 5.x
- ESLint to 9.x
-
Add prototype pollution deep validation
- Implement recursive key checking
- Add tests for nested prototype pollution attempts
-
Expression parser hardening
- Add expression complexity limits
- Implement execution timeout
- Add function whitelist mechanism
-
Rule structure validation
- Add
validateRule()function - Implement max nesting depth
- Add comprehensive rule schema validation
- Add
-
Improve error handling
- Restore stack traces for non-UserError exceptions
- Add error cause tracking
- Implement structured error logging
-
TypeScript strictness
- Remove all
@ts-expect-errorcomments - Eliminate
anytypes - Add comprehensive type tests
- Remove all
-
Performance optimization
- Add memoization for pure functions
- Benchmark and document performance characteristics
- Add performance regression tests
-
Documentation improvements
- Security considerations section
- Performance guidelines
- Error handling patterns
| ID | Severity | Category | Description |
|---|---|---|---|
| MEDIUM-01 | Medium | Code Quality | Inadequate Error Handling |
| MEDIUM-02 | Medium | Code Quality | Inconsistent TypeScript Strictness |
| MEDIUM-03 | Medium | Security | Reserved Key Validation Bypass |
| LOW-01 | Low | Performance | Missing Memoization |
| LOW-02 | Low | Documentation | Documentation Gaps |
- Prototype pollution protection (deep validation)
- Expression complexity limits
- Execution timeout mechanism
- Input validation for rule structures
- Dependency security updates
- CI/CD pipeline security updates
- Error handling improvements
- Reserved key protection enhancement
src/
βββ index.ts # Main rule engine (560 lines)
βββ index.test.ts # Main test file (526 lines)
βββ arrays.test.ts # Array method tests (127 lines)
βββ types.ts # Type exports (1 line)
βββ expression-language/
β βββ index.ts # Expression parser config (629 lines)
β βββ utils.ts # Expression utilities (213 lines)
βββ utils/
βββ errors.ts # UserError class (15 lines)
βββ utils.ts # Type utilities (43 lines)
βββ performance.ts # Performance wrapper
βββ mockDateHelper.ts # Test utility
package.json- Dependencies and scriptstsconfig.json- TypeScript configurationtsup.config.ts- Build configuration.eslintrc.js- ESLint configuration.prettierrc- Code formattingjest.config.ts- Test configuration.github/workflows/test.yml- CI/CD pipeline
The rules-machine library is a solid, well-architected rules engine with good foundations. The primary concerns are:
- Outdated dependencies and CI/CD - Critical security and compatibility risk
- Security hardening - Needs deeper prototype pollution protection and expression sandboxing
- TypeScript rigor - Some technical debt in type definitions
Addressing the critical findings should be prioritized, followed by security hardening. The codebase architecture is sound and supports incremental improvements without requiring major refactoring.
Generated: April 6, 2026 Next Review: Recommend quarterly security audits after remediation