Rule Engine API
The rule engine evaluates validation rules against parsed configuration AST. This page covers the API for running rules programmatically.
Installation
npm install @sentriflow/core
npm install @sentriflow/rules-default # Optional: default rulesBasic Usage
import { parse, evaluate } from '@sentriflow/core';
import { allRules } from '@sentriflow/rules-default';
const config = `
hostname Router1
interface GigabitEthernet0/0
ip address 192.168.1.1 255.255.255.0
`;
const ast = parse(config, 'cisco-ios');
const results = evaluate(ast, allRules);
for (const result of results) {
if (!result.passed) {
console.log(`[${result.level}] ${result.ruleId}: ${result.message}`);
}
}evaluate()
Evaluate rules against a parsed AST.
Signature
function evaluate(
ast: ConfigNode[],
rules: IRule[],
options?: EvaluateOptions
): RuleResult[]Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
ast | ConfigNode[] | Yes | Parsed configuration AST |
rules | IRule[] | Yes | Rules to evaluate |
options | EvaluateOptions | No | Evaluation options |
EvaluateOptions
interface EvaluateOptions {
/** Vendor for filtering vendor-specific rules */
vendor?: RuleVendor;
/** Minimum severity to include in results */
minimumSeverity?: 'error' | 'warning' | 'info';
/** Rule IDs to skip */
disabledRules?: string[];
/** Categories to skip */
disabledCategories?: string[];
/** Stop after first failure */
failFast?: boolean;
/** Timeout per rule check in ms */
timeout?: number;
/** Include passed results (not just failures) */
includePassedResults?: boolean;
}Returns
Returns an array of RuleResult objects.
Example
import { parse, evaluate } from '@sentriflow/core';
import { allRules, getRulesByVendor } from '@sentriflow/rules-default';
const config = loadConfig('./router.conf');
const ast = parse(config, 'cisco-ios');
// Evaluate all rules
const allResults = evaluate(ast, allRules);
// Evaluate only Cisco rules
const ciscoRules = getRulesByVendor('cisco-ios');
const ciscoResults = evaluate(ast, ciscoRules);
// Evaluate with options
const filteredResults = evaluate(ast, allRules, {
vendor: 'cisco-ios',
minimumSeverity: 'warning',
disabledRules: ['NET-DOC-001'],
includePassedResults: false,
});RuleResult Interface
interface RuleResult {
/** Whether the rule check passed */
passed: boolean;
/** Result message */
message: string;
/** Rule ID that generated this result */
ruleId: string;
/** Node ID that was checked */
nodeId: string;
/** Severity level */
level: 'error' | 'warning' | 'info';
/** Optional remediation text */
remediation?: string;
/** Source location */
loc?: {
startLine: number;
endLine: number;
};
}evaluateFile()
Parse and evaluate a configuration file in one call.
Signature
async function evaluateFile(
filePath: string,
rules: IRule[],
options?: EvaluateFileOptions
): Promise<RuleResult[]>Example
import { evaluateFile } from '@sentriflow/core';
import { allRules } from '@sentriflow/rules-default';
const results = await evaluateFile('./router.conf', allRules, {
vendor: 'cisco-ios',
minimumSeverity: 'warning',
});
console.log(`Found ${results.filter(r => !r.passed).length} issues`);evaluateDirectory()
Evaluate all configuration files in a directory.
Signature
async function evaluateDirectory(
dirPath: string,
rules: IRule[],
options?: EvaluateDirectoryOptions
): Promise<Map<string, RuleResult[]>>EvaluateDirectoryOptions
interface EvaluateDirectoryOptions extends EvaluateOptions {
/** Glob patterns to include */
include?: string[];
/** Glob patterns to exclude */
exclude?: string[];
/** Recurse into subdirectories */
recursive?: boolean;
/** Max concurrent file evaluations */
concurrency?: number;
}Example
import { evaluateDirectory } from '@sentriflow/core';
import { allRules } from '@sentriflow/rules-default';
const results = await evaluateDirectory('./configs', allRules, {
vendor: 'cisco-ios',
include: ['**/*.conf', '**/*.cfg'],
exclude: ['**/backup/**'],
recursive: true,
concurrency: 4,
});
for (const [file, fileResults] of results) {
const failures = fileResults.filter(r => !r.passed);
if (failures.length > 0) {
console.log(`\n${file}:`);
for (const result of failures) {
console.log(` [${result.level}] ${result.ruleId}: ${result.message}`);
}
}
}RuleEngine Class
For advanced use cases, use the RuleEngine class directly:
import { RuleEngine } from '@sentriflow/core';
import { allRules } from '@sentriflow/rules-default';
const engine = new RuleEngine({
rules: allRules,
defaultVendor: 'cisco-ios',
timeout: 100,
});
// Add custom rules
engine.addRule(myCustomRule);
engine.addRules(myRulePack.rules);
// Remove rules
engine.removeRule('NET-DOC-001');
// Evaluate
const results = engine.evaluate(ast);
// Get rule info
const rule = engine.getRule('NET-AUTH-001');
const rulesByCategory = engine.getRulesByCategory('Authentication');RuleEngine Methods
| Method | Description |
|---|---|
addRule(rule) | Add a single rule |
addRules(rules) | Add multiple rules |
removeRule(id) | Remove a rule by ID |
getRule(id) | Get rule by ID |
getRules() | Get all rules |
getRulesByVendor(vendor) | Get rules for vendor |
getRulesByCategory(cat) | Get rules by category |
evaluate(ast, options?) | Evaluate rules against AST |
Custom Rule Registration
Register custom rules programmatically:
import { RuleEngine } from '@sentriflow/core';
import type { IRule } from '@sentriflow/core';
const customRule: IRule = {
id: 'ORG-SEC-001',
selector: 'interface',
vendor: 'cisco-ios',
metadata: {
level: 'warning',
obu: 'Security',
owner: 'security@example.com',
description: 'Custom security check',
},
check(node) {
// Validation logic
return { passed: true, /* ... */ };
},
};
const engine = new RuleEngine();
engine.addRule(customRule);Rule Packs
Load rules from rule packs:
import { loadRulePack, RuleEngine } from '@sentriflow/core';
// Load from file
const pack = await loadRulePack('./custom-rules.json');
// Load from package
const pack2 = await loadRulePack('@myorg/security-rules');
// Use with engine
const engine = new RuleEngine({
rules: pack.rules,
});
// Handle rule conflicts
engine.addRulePack(pack, {
conflictResolution: 'override', // or 'skip', 'error'
});Sandboxed Execution
For untrusted rules, use sandboxed execution:
import { SandboxedExecutor } from '@sentriflow/core';
const executor = new SandboxedExecutor({
timeout: 100, // 100ms timeout per check
memoryLimit: 50 * 1024 * 1024, // 50MB memory limit
});
const results = await executor.evaluate(ast, untrustedRules);Sandboxed execution has ~10x overhead. Only use for untrusted rules.
Result Formatting
Format results for different outputs:
import { formatResults } from '@sentriflow/core';
const results = evaluate(ast, rules);
// Human-readable text
const text = formatResults(results, 'text');
// JSON
const json = formatResults(results, 'json');
// SARIF
const sarif = formatResults(results, 'sarif', {
tool: {
name: 'SentriFlow',
version: '1.0.0',
},
});Performance Tips
- Filter rules by vendor before evaluation
- Use
failFast: truefor quick validation checks - Disable rules you don’t need with
disabledRules - Set appropriate timeout for custom rules
- Use streaming for large configurations
Next Steps
- Parser API - Parsing configurations
- ConfigNode API - Working with AST
- TypeScript Rules - Creating custom rules
Last updated on