Skip to Content
APIConfigNode API: Navigate and Query Network Config AST

ConfigNode API

The ConfigNode interface represents a parsed configuration element in SentriFlow’s AST. This page covers how to navigate and query configuration nodes.

ConfigNode Interface

interface ConfigNode { /** Node identifier (command prefix) */ id: string; /** Node type */ type: 'command' | 'section' | 'comment' | 'virtual_root'; /** Command parameters (split by whitespace) */ params: string[]; /** Child nodes (for sections) */ children: ConfigNode[]; /** Parent node reference */ parent?: ConfigNode; /** Source location */ loc: { startLine: number; endLine: number; }; /** Raw text of the command */ raw: string; }

Properties

id

The node identifier, which is the command prefix used for selector matching.

// For "interface GigabitEthernet0/0" node.id === 'interface GigabitEthernet0/0' // For "ip address 192.168.1.1 255.255.255.0" node.id === 'ip address 192.168.1.1 255.255.255.0'

The id is the full command including all parameters, not just the keyword.

type

The node type:

TypeDescription
commandA single configuration command
sectionA configuration block with children
commentA comment line
virtual_rootSynthetic root node (internal use)

params

Command split by whitespace:

// For "ip address 192.168.1.1 255.255.255.0" node.params === ['ip', 'address', '192.168.1.1', '255.255.255.0'] // For "interface GigabitEthernet0/0" node.params === ['interface', 'GigabitEthernet0/0']

children

Child nodes for section-type nodes:

const interfaceNode = ast.find(n => n.id.startsWith('interface')); for (const child of interfaceNode.children) { console.log(child.id); // "ip address ...", "no shutdown", etc. }

parent

Reference to parent node (undefined for root nodes):

const ipAddressNode = ast.find(n => n.id.startsWith('ip address')); console.log(ipAddressNode?.parent?.id); // "interface GigabitEthernet0/0"

loc

Source location in the original configuration:

const node = ast[0]; console.log(`Lines ${node.loc.startLine}-${node.loc.endLine}`);

raw

Original text of the configuration line:

// For indented command node.raw === ' ip address 192.168.1.1 255.255.255.0'

Parsing Configuration

Use the SchemaAwareParser class to parse configuration text into an AST:

import { SchemaAwareParser } from '@sentriflow/core'; const config = ` interface GigabitEthernet0/0 ip address 192.168.1.1 255.255.255.0 no shutdown `; const parser = new SchemaAwareParser(); const ast = parser.parse(config);

Traversing the AST

Since the AST is returned as an array of ConfigNode objects, you can use standard JavaScript array methods to traverse and query it.

Finding Nodes

Use Array.filter() to find all nodes matching a condition:

// Find all interface sections const interfaces = ast.filter(n => n.type === 'section' && n.id.startsWith('interface') ); // Find all BGP router sections const bgpRouters = ast.filter(n => n.id.startsWith('router bgp'));

Use Array.find() to find the first matching node:

const interfaceNode = ast.find(n => n.type === 'section' && n.id.startsWith('interface') );

Query Helpers

SentriFlow provides helper functions for common node queries. Import them from @sentriflow/core:

import { hasChildCommand, getChildCommand, getChildCommands } from '@sentriflow/core';

hasChildCommand()

Check if a node has a child matching a prefix:

import { hasChildCommand } from '@sentriflow/core'; const hasDescription = hasChildCommand(interfaceNode, 'description'); const isShutdown = hasChildCommand(interfaceNode, 'shutdown');

getChildCommand()

Get a child node by prefix:

import { getChildCommand } from '@sentriflow/core'; const descNode = getChildCommand(interfaceNode, 'description'); if (descNode) { const description = descNode.params.slice(1).join(' '); }

getChildCommands()

Get all children matching a prefix:

import { getChildCommands } from '@sentriflow/core'; const aclEntries = getChildCommands(aclNode, 'permit'); const denyEntries = getChildCommands(aclNode, 'deny');

Working with Parameters

Access parameter values directly from the params array:

// For "ip address 192.168.1.1 255.255.255.0" const ip = node.params[2]; // "192.168.1.1" const mask = node.params[3]; // "255.255.255.0"

For more complex parameter extraction, use standard array methods:

// Find value after a keyword const params = node.params; const vlanIndex = params.indexOf('vlan'); const vlanId = vlanIndex !== -1 ? params[vlanIndex + 1] : undefined;

Selector Matching

Selectors use case-insensitive prefix matching:

// Simple selectors 'interface' // Matches: "interface ...", "Interface ...", "INTERFACE ..." 'hostname' // Matches: "hostname ...", "Hostname ..." // Multi-word selectors 'router bgp' // Matches: "router bgp 65000" 'ip access-list' // Matches: "ip access-list extended ..."

Example: Finding Security Issues

import { SchemaAwareParser, hasChildCommand, getChildCommand } from '@sentriflow/core'; const config = ` interface GigabitEthernet0/0 ip address 192.168.1.1 255.255.255.0 no shutdown ! interface GigabitEthernet0/1 description Uplink ip address 10.0.0.1 255.255.255.0 no shutdown `; const parser = new SchemaAwareParser(); const ast = parser.parse(config); // Find interfaces without descriptions const interfaces = ast.filter(n => n.id.startsWith('interface')); const missingDescription = interfaces.filter(iface => !hasChildCommand(iface, 'description') ); for (const iface of missingDescription) { console.log(`Missing description: ${iface.id} (line ${iface.loc.startLine})`); }

Example: Cross-Reference Check

import { SchemaAwareParser } from '@sentriflow/core'; // Check if referenced IPs exist on interfaces function checkIpReferences(ast: ConfigNode[]): string[] { const issues: string[] = []; // Get all interface IPs const interfaceIps = new Set<string>(); const interfaces = ast.filter(n => n.id.startsWith('interface')); for (const iface of interfaces) { const ipNode = iface.children.find(c => c.id.startsWith('ip address')); if (ipNode) { const ip = ipNode.params[2]; if (ip) interfaceIps.add(ip); } } // Check route next-hops const routes = ast.filter(n => n.id.startsWith('ip route')); for (const route of routes) { const nextHop = route.params[4]; // ip route <dest> <mask> <next-hop> if (nextHop && !interfaceIps.has(nextHop)) { // Next-hop not on local interface (might be external - just log) console.log(`Route next-hop ${nextHop} not on local interface`); } } return issues; }

Next Steps

Last updated on