@sentriflow/core 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/LICENSE +190 -0
  2. package/README.md +86 -0
  3. package/package.json +60 -0
  4. package/src/constants.ts +77 -0
  5. package/src/engine/RuleExecutor.ts +256 -0
  6. package/src/engine/Runner.ts +312 -0
  7. package/src/engine/SandboxedExecutor.ts +208 -0
  8. package/src/errors.ts +88 -0
  9. package/src/helpers/arista/helpers.ts +1220 -0
  10. package/src/helpers/arista/index.ts +12 -0
  11. package/src/helpers/aruba/helpers.ts +637 -0
  12. package/src/helpers/aruba/index.ts +13 -0
  13. package/src/helpers/cisco/helpers.ts +534 -0
  14. package/src/helpers/cisco/index.ts +11 -0
  15. package/src/helpers/common/helpers.ts +265 -0
  16. package/src/helpers/common/index.ts +5 -0
  17. package/src/helpers/common/validation.ts +280 -0
  18. package/src/helpers/cumulus/helpers.ts +676 -0
  19. package/src/helpers/cumulus/index.ts +12 -0
  20. package/src/helpers/extreme/helpers.ts +422 -0
  21. package/src/helpers/extreme/index.ts +12 -0
  22. package/src/helpers/fortinet/helpers.ts +892 -0
  23. package/src/helpers/fortinet/index.ts +12 -0
  24. package/src/helpers/huawei/helpers.ts +790 -0
  25. package/src/helpers/huawei/index.ts +11 -0
  26. package/src/helpers/index.ts +53 -0
  27. package/src/helpers/juniper/helpers.ts +756 -0
  28. package/src/helpers/juniper/index.ts +12 -0
  29. package/src/helpers/mikrotik/helpers.ts +722 -0
  30. package/src/helpers/mikrotik/index.ts +12 -0
  31. package/src/helpers/nokia/helpers.ts +856 -0
  32. package/src/helpers/nokia/index.ts +11 -0
  33. package/src/helpers/paloalto/helpers.ts +939 -0
  34. package/src/helpers/paloalto/index.ts +12 -0
  35. package/src/helpers/vyos/helpers.ts +429 -0
  36. package/src/helpers/vyos/index.ts +12 -0
  37. package/src/index.ts +30 -0
  38. package/src/json-rules/ExpressionEvaluator.ts +292 -0
  39. package/src/json-rules/HelperRegistry.ts +177 -0
  40. package/src/json-rules/JsonRuleCompiler.ts +339 -0
  41. package/src/json-rules/JsonRuleValidator.ts +371 -0
  42. package/src/json-rules/index.ts +97 -0
  43. package/src/json-rules/schema.json +350 -0
  44. package/src/json-rules/types.ts +303 -0
  45. package/src/pack-loader/PackLoader.ts +332 -0
  46. package/src/pack-loader/index.ts +17 -0
  47. package/src/pack-loader/types.ts +135 -0
  48. package/src/parser/IncrementalParser.ts +527 -0
  49. package/src/parser/Sanitizer.ts +104 -0
  50. package/src/parser/SchemaAwareParser.ts +504 -0
  51. package/src/parser/VendorSchema.ts +72 -0
  52. package/src/parser/vendors/arista-eos.ts +206 -0
  53. package/src/parser/vendors/aruba-aoscx.ts +123 -0
  54. package/src/parser/vendors/aruba-aosswitch.ts +113 -0
  55. package/src/parser/vendors/aruba-wlc.ts +173 -0
  56. package/src/parser/vendors/cisco-ios.ts +110 -0
  57. package/src/parser/vendors/cisco-nxos.ts +107 -0
  58. package/src/parser/vendors/cumulus-linux.ts +161 -0
  59. package/src/parser/vendors/extreme-exos.ts +154 -0
  60. package/src/parser/vendors/extreme-voss.ts +167 -0
  61. package/src/parser/vendors/fortinet-fortigate.ts +217 -0
  62. package/src/parser/vendors/huawei-vrp.ts +192 -0
  63. package/src/parser/vendors/index.ts +1521 -0
  64. package/src/parser/vendors/juniper-junos.ts +230 -0
  65. package/src/parser/vendors/mikrotik-routeros.ts +274 -0
  66. package/src/parser/vendors/nokia-sros.ts +251 -0
  67. package/src/parser/vendors/paloalto-panos.ts +264 -0
  68. package/src/parser/vendors/vyos-vyos.ts +454 -0
  69. package/src/types/ConfigNode.ts +72 -0
  70. package/src/types/DeclarativeRule.ts +158 -0
  71. package/src/types/IRule.ts +270 -0
@@ -0,0 +1,454 @@
1
+ // packages/core/src/parser/vendors/vyos-vyos.ts
2
+
3
+ import type { VendorSchema } from '../VendorSchema';
4
+
5
+ /**
6
+ * VyOS/Ubiquiti EdgeOS configuration schema.
7
+ *
8
+ * VyOS (and Ubiquiti EdgeOS which is based on Vyatta/VyOS) uses a hierarchical
9
+ * configuration model that can be displayed in multiple formats:
10
+ * 1. Set command format (CLI style): "set interfaces ethernet eth0 address 192.168.1.1/24"
11
+ * 2. Hierarchical display format with braces: "interfaces { ethernet eth0 { ... } }"
12
+ *
13
+ * Key characteristics:
14
+ * - Brace-based hierarchy in display mode (like JunOS)
15
+ * - Set-style commands: "set interfaces ethernet eth0 address 192.168.1.1/24"
16
+ * - Delete commands: "delete interfaces ethernet eth0 address 192.168.1.1/24"
17
+ * - Comments: Block comments or added via "comment" command
18
+ * - Interface naming: eth0, eth1, bond0, br0, wg0, vti0, pppoe0, tun0
19
+ * - Zone-based firewall with named rulesets
20
+ * - VPN support: IPsec, OpenVPN, WireGuard, L2TP
21
+ *
22
+ * Configuration structure (hierarchical format):
23
+ * ```
24
+ * interfaces {
25
+ * ethernet eth0 {
26
+ * address 192.168.1.1/24
27
+ * description "WAN Interface"
28
+ * hw-id 00:0c:29:xx:xx:xx
29
+ * }
30
+ * loopback lo {
31
+ * }
32
+ * }
33
+ * system {
34
+ * host-name vyos-router
35
+ * login {
36
+ * user vyos {
37
+ * authentication {
38
+ * encrypted-password "$6$..."
39
+ * }
40
+ * }
41
+ * }
42
+ * }
43
+ * ```
44
+ *
45
+ * Set command format:
46
+ * ```
47
+ * set interfaces ethernet eth0 address '192.168.1.1/24'
48
+ * set interfaces ethernet eth0 description 'WAN Interface'
49
+ * set system host-name vyos-router
50
+ * set firewall name WAN_IN default-action drop
51
+ * set firewall name WAN_IN rule 10 action accept
52
+ * set firewall name WAN_IN rule 10 state established enable
53
+ * ```
54
+ */
55
+ export const VyOSSchema: VendorSchema = {
56
+ id: 'vyos',
57
+ name: 'VyOS/EdgeOS',
58
+ useBraceHierarchy: true,
59
+
60
+ commentPatterns: [
61
+ /^\/\*.*\*\/$/, // Block comments /* ... */
62
+ /^\/\*.*$/, // Multi-line comment start /* ...
63
+ /^.*\*\/$/, // Multi-line comment end ... */
64
+ /^#/, // Hash comments (some modes)
65
+ ],
66
+ sectionDelimiter: '}',
67
+
68
+ blockStarters: [
69
+ // ============ DEPTH 0: Top-level configuration stanzas ============
70
+
71
+ // System configuration (hostname, login, dns, ntp, syslog)
72
+ { pattern: /^system\s*\{?$/i, depth: 0 },
73
+
74
+ // Interfaces (ethernet, loopback, bonding, bridge, wireguard, etc.)
75
+ { pattern: /^interfaces\s*\{?$/i, depth: 0 },
76
+
77
+ // Firewall configuration (zones, groups, rules)
78
+ { pattern: /^firewall\s*\{?$/i, depth: 0 },
79
+
80
+ // NAT configuration
81
+ { pattern: /^nat\s*\{?$/i, depth: 0 },
82
+
83
+ // Routing protocols
84
+ { pattern: /^protocols\s*\{?$/i, depth: 0 },
85
+
86
+ // Policy (route-map, prefix-list, as-path, community-list)
87
+ { pattern: /^policy\s*\{?$/i, depth: 0 },
88
+
89
+ // Service configuration (dhcp-server, dns, ssh, https, etc.)
90
+ { pattern: /^service\s*\{?$/i, depth: 0 },
91
+
92
+ // VPN configuration (ipsec, openvpn, l2tp, pptp, wireguard)
93
+ { pattern: /^vpn\s*\{?$/i, depth: 0 },
94
+
95
+ // High availability (VRRP, conntrack-sync)
96
+ { pattern: /^high-availability\s*\{?$/i, depth: 0 },
97
+
98
+ // QoS (traffic shaping, policies)
99
+ { pattern: /^traffic-policy\s*\{?$/i, depth: 0 },
100
+ { pattern: /^qos\s*\{?$/i, depth: 0 },
101
+
102
+ // Container (podman/docker containers in VyOS 1.4+)
103
+ { pattern: /^container\s*\{?$/i, depth: 0 },
104
+
105
+ // Load balancing (WAN load balancing, reverse proxy)
106
+ { pattern: /^load-balancing\s*\{?$/i, depth: 0 },
107
+
108
+ // VRRP (standalone in older versions)
109
+ { pattern: /^vrrp\s*\{?$/i, depth: 0 },
110
+
111
+ // PKI (certificates, CA)
112
+ { pattern: /^pki\s*\{?$/i, depth: 0 },
113
+
114
+ // Set command format (flat configuration)
115
+ { pattern: /^set\s+system\s+/i, depth: 0 },
116
+ { pattern: /^set\s+interfaces\s+/i, depth: 0 },
117
+ { pattern: /^set\s+firewall\s+/i, depth: 0 },
118
+ { pattern: /^set\s+nat\s+/i, depth: 0 },
119
+ { pattern: /^set\s+protocols\s+/i, depth: 0 },
120
+ { pattern: /^set\s+policy\s+/i, depth: 0 },
121
+ { pattern: /^set\s+service\s+/i, depth: 0 },
122
+ { pattern: /^set\s+vpn\s+/i, depth: 0 },
123
+ { pattern: /^set\s+high-availability\s+/i, depth: 0 },
124
+ { pattern: /^set\s+traffic-policy\s+/i, depth: 0 },
125
+ { pattern: /^set\s+qos\s+/i, depth: 0 },
126
+ { pattern: /^set\s+container\s+/i, depth: 0 },
127
+ { pattern: /^set\s+load-balancing\s+/i, depth: 0 },
128
+ { pattern: /^set\s+pki\s+/i, depth: 0 },
129
+
130
+ // Delete commands (same structure as set)
131
+ { pattern: /^delete\s+/i, depth: 0 },
132
+
133
+ // ============ DEPTH 1: Inside top-level stanzas ============
134
+
135
+ // Inside system
136
+ { pattern: /^host-name\s+/i, depth: 1 },
137
+ { pattern: /^login\s*\{?$/i, depth: 1 },
138
+ { pattern: /^name-server\s+/i, depth: 1 },
139
+ { pattern: /^ntp\s*\{?$/i, depth: 1 },
140
+ { pattern: /^syslog\s*\{?$/i, depth: 1 },
141
+ { pattern: /^time-zone\s+/i, depth: 1 },
142
+ { pattern: /^console\s*\{?$/i, depth: 1 },
143
+ { pattern: /^config-management\s*\{?$/i, depth: 1 },
144
+ { pattern: /^conntrack\s*\{?$/i, depth: 1 },
145
+ { pattern: /^domain-name\s+/i, depth: 1 },
146
+ { pattern: /^flow-accounting\s*\{?$/i, depth: 1 },
147
+ { pattern: /^options\s*\{?$/i, depth: 1 },
148
+ { pattern: /^static-host-mapping\s*\{?$/i, depth: 1 },
149
+ { pattern: /^task-scheduler\s*\{?$/i, depth: 1 },
150
+
151
+ // Inside interfaces - interface types
152
+ { pattern: /^ethernet\s+\S+\s*\{?$/i, depth: 1 },
153
+ { pattern: /^loopback\s+\S+\s*\{?$/i, depth: 1 },
154
+ { pattern: /^bonding\s+\S+\s*\{?$/i, depth: 1 },
155
+ { pattern: /^bridge\s+\S+\s*\{?$/i, depth: 1 },
156
+ { pattern: /^wireguard\s+\S+\s*\{?$/i, depth: 1 },
157
+ { pattern: /^openvpn\s+\S+\s*\{?$/i, depth: 1 },
158
+ { pattern: /^vti\s+\S+\s*\{?$/i, depth: 1 },
159
+ { pattern: /^tunnel\s+\S+\s*\{?$/i, depth: 1 },
160
+ { pattern: /^l2tpv3\s+\S+\s*\{?$/i, depth: 1 },
161
+ { pattern: /^pppoe\s+\S+\s*\{?$/i, depth: 1 },
162
+ { pattern: /^vxlan\s+\S+\s*\{?$/i, depth: 1 },
163
+ { pattern: /^macsec\s+\S+\s*\{?$/i, depth: 1 },
164
+ { pattern: /^pseudo-ethernet\s+\S+\s*\{?$/i, depth: 1 },
165
+ { pattern: /^wireless\s+\S+\s*\{?$/i, depth: 1 },
166
+ { pattern: /^wwan\s+\S+\s*\{?$/i, depth: 1 },
167
+ { pattern: /^dummy\s+\S+\s*\{?$/i, depth: 1 },
168
+
169
+ // Inside firewall
170
+ { pattern: /^all-ping\s+/i, depth: 1 },
171
+ { pattern: /^broadcast-ping\s+/i, depth: 1 },
172
+ { pattern: /^config-trap\s+/i, depth: 1 },
173
+ { pattern: /^group\s*\{?$/i, depth: 1 },
174
+ { pattern: /^ipv6-name\s+\S+\s*\{?$/i, depth: 1 },
175
+ { pattern: /^ipv6-src-route\s+/i, depth: 1 },
176
+ { pattern: /^ip-src-route\s+/i, depth: 1 },
177
+ { pattern: /^log-martians\s+/i, depth: 1 },
178
+ { pattern: /^name\s+\S+\s*\{?$/i, depth: 1 },
179
+ { pattern: /^options\s*\{?$/i, depth: 1 },
180
+ { pattern: /^receive-redirects\s+/i, depth: 1 },
181
+ { pattern: /^send-redirects\s+/i, depth: 1 },
182
+ { pattern: /^source-validation\s+/i, depth: 1 },
183
+ { pattern: /^state-policy\s*\{?$/i, depth: 1 },
184
+ { pattern: /^syn-cookies\s+/i, depth: 1 },
185
+ { pattern: /^twa-hazards-protection\s+/i, depth: 1 },
186
+ { pattern: /^zone\s+\S+\s*\{?$/i, depth: 1 },
187
+ // VyOS 1.4+ firewall structure
188
+ { pattern: /^ipv4\s*\{?$/i, depth: 1 },
189
+ { pattern: /^ipv6\s*\{?$/i, depth: 1 },
190
+
191
+ // Inside nat
192
+ { pattern: /^source\s*\{?$/i, depth: 1 },
193
+ { pattern: /^destination\s*\{?$/i, depth: 1 },
194
+ { pattern: /^nptv6\s*\{?$/i, depth: 1 },
195
+
196
+ // Inside protocols
197
+ { pattern: /^bgp\s*\{?$/i, depth: 1 },
198
+ { pattern: /^ospf\s*\{?$/i, depth: 1 },
199
+ { pattern: /^ospfv3\s*\{?$/i, depth: 1 },
200
+ { pattern: /^rip\s*\{?$/i, depth: 1 },
201
+ { pattern: /^ripng\s*\{?$/i, depth: 1 },
202
+ { pattern: /^isis\s*\{?$/i, depth: 1 },
203
+ { pattern: /^static\s*\{?$/i, depth: 1 },
204
+ { pattern: /^bfd\s*\{?$/i, depth: 1 },
205
+ { pattern: /^igmp-proxy\s*\{?$/i, depth: 1 },
206
+ { pattern: /^mpls\s*\{?$/i, depth: 1 },
207
+ { pattern: /^nhrp\s*\{?$/i, depth: 1 },
208
+ { pattern: /^pim\s*\{?$/i, depth: 1 },
209
+ { pattern: /^rpki\s*\{?$/i, depth: 1 },
210
+ { pattern: /^segment-routing\s*\{?$/i, depth: 1 },
211
+
212
+ // Inside policy
213
+ { pattern: /^access-list\s+\S+\s*\{?$/i, depth: 1 },
214
+ { pattern: /^access-list6\s+\S+\s*\{?$/i, depth: 1 },
215
+ { pattern: /^as-path-list\s+\S+\s*\{?$/i, depth: 1 },
216
+ { pattern: /^community-list\s+\S+\s*\{?$/i, depth: 1 },
217
+ { pattern: /^extcommunity-list\s+\S+\s*\{?$/i, depth: 1 },
218
+ { pattern: /^large-community-list\s+\S+\s*\{?$/i, depth: 1 },
219
+ { pattern: /^prefix-list\s+\S+\s*\{?$/i, depth: 1 },
220
+ { pattern: /^prefix-list6\s+\S+\s*\{?$/i, depth: 1 },
221
+ { pattern: /^route-map\s+\S+\s*\{?$/i, depth: 1 },
222
+ { pattern: /^local-route\s*\{?$/i, depth: 1 },
223
+ { pattern: /^local-route6\s*\{?$/i, depth: 1 },
224
+
225
+ // Inside service
226
+ { pattern: /^dhcp-server\s*\{?$/i, depth: 1 },
227
+ { pattern: /^dhcpv6-server\s*\{?$/i, depth: 1 },
228
+ { pattern: /^dhcp-relay\s*\{?$/i, depth: 1 },
229
+ { pattern: /^dhcpv6-relay\s*\{?$/i, depth: 1 },
230
+ { pattern: /^dns\s*\{?$/i, depth: 1 },
231
+ { pattern: /^https\s*\{?$/i, depth: 1 },
232
+ { pattern: /^ssh\s*\{?$/i, depth: 1 },
233
+ { pattern: /^snmp\s*\{?$/i, depth: 1 },
234
+ { pattern: /^lldp\s*\{?$/i, depth: 1 },
235
+ { pattern: /^ntp\s*\{?$/i, depth: 1 },
236
+ { pattern: /^router-advert\s*\{?$/i, depth: 1 },
237
+ { pattern: /^tftp-server\s*\{?$/i, depth: 1 },
238
+ { pattern: /^mdns\s*\{?$/i, depth: 1 },
239
+ { pattern: /^monitoring\s*\{?$/i, depth: 1 },
240
+ { pattern: /^webproxy\s*\{?$/i, depth: 1 },
241
+ { pattern: /^broadcast-relay\s*\{?$/i, depth: 1 },
242
+ { pattern: /^ids\s*\{?$/i, depth: 1 },
243
+ { pattern: /^ipoe-server\s*\{?$/i, depth: 1 },
244
+ { pattern: /^pppoe-server\s*\{?$/i, depth: 1 },
245
+ { pattern: /^console-server\s*\{?$/i, depth: 1 },
246
+
247
+ // Inside vpn
248
+ { pattern: /^ipsec\s*\{?$/i, depth: 1 },
249
+ { pattern: /^l2tp\s*\{?$/i, depth: 1 },
250
+ { pattern: /^pptp\s*\{?$/i, depth: 1 },
251
+ { pattern: /^openconnect\s*\{?$/i, depth: 1 },
252
+ { pattern: /^sstp\s*\{?$/i, depth: 1 },
253
+
254
+ // Inside high-availability
255
+ { pattern: /^vrrp\s*\{?$/i, depth: 1 },
256
+
257
+ // ============ DEPTH 2: Nested inside depth-1 blocks ============
258
+
259
+ // Inside login
260
+ { pattern: /^user\s+\S+\s*\{?$/i, depth: 2 },
261
+ { pattern: /^radius\s*\{?$/i, depth: 2 },
262
+ { pattern: /^tacacs\s*\{?$/i, depth: 2 },
263
+
264
+ // Inside NTP
265
+ { pattern: /^server\s+\S+\s*\{?$/i, depth: 2 },
266
+ { pattern: /^allow-client\s*\{?$/i, depth: 2 },
267
+
268
+ // Inside syslog
269
+ { pattern: /^global\s*\{?$/i, depth: 2 },
270
+ { pattern: /^host\s+\S+\s*\{?$/i, depth: 2 },
271
+ { pattern: /^console\s*\{?$/i, depth: 2 },
272
+
273
+ // Inside interface definitions (vif, address, firewall bindings)
274
+ { pattern: /^vif\s+\d+\s*\{?$/i, depth: 2 },
275
+ { pattern: /^vif-s\s+\d+\s*\{?$/i, depth: 2 }, // QinQ outer VLAN
276
+
277
+ // Inside firewall group
278
+ { pattern: /^address-group\s+\S+\s*\{?$/i, depth: 2 },
279
+ { pattern: /^ipv6-address-group\s+\S+\s*\{?$/i, depth: 2 },
280
+ { pattern: /^network-group\s+\S+\s*\{?$/i, depth: 2 },
281
+ { pattern: /^ipv6-network-group\s+\S+\s*\{?$/i, depth: 2 },
282
+ { pattern: /^interface-group\s+\S+\s*\{?$/i, depth: 2 },
283
+ { pattern: /^mac-group\s+\S+\s*\{?$/i, depth: 2 },
284
+ { pattern: /^port-group\s+\S+\s*\{?$/i, depth: 2 },
285
+ { pattern: /^domain-group\s+\S+\s*\{?$/i, depth: 2 },
286
+
287
+ // Inside firewall ruleset (name/ipv6-name)
288
+ { pattern: /^default-action\s+/i, depth: 2 },
289
+ { pattern: /^enable-default-log\s*$/i, depth: 2 },
290
+ { pattern: /^rule\s+\d+\s*\{?$/i, depth: 2 },
291
+
292
+ // Inside firewall zone
293
+ { pattern: /^default-action\s+/i, depth: 2 },
294
+ { pattern: /^from\s+\S+\s*\{?$/i, depth: 2 },
295
+ { pattern: /^interface\s+/i, depth: 2 },
296
+ { pattern: /^local-zone\s*$/i, depth: 2 },
297
+
298
+ // Inside NAT source/destination
299
+ { pattern: /^rule\s+\d+\s*\{?$/i, depth: 2 },
300
+
301
+ // Inside BGP (VyOS 1.4+ uses 'system-as' inside bgp block)
302
+ { pattern: /^address-family\s*\{?$/i, depth: 2 },
303
+ { pattern: /^neighbor\s+\S+\s*\{?$/i, depth: 2 },
304
+ { pattern: /^peer-group\s+\S+\s*\{?$/i, depth: 2 },
305
+ { pattern: /^parameters\s*\{?$/i, depth: 2 },
306
+ { pattern: /^listen\s*\{?$/i, depth: 2 },
307
+
308
+ // Inside OSPF
309
+ { pattern: /^area\s+\S+\s*\{?$/i, depth: 2 },
310
+ { pattern: /^default-information\s*\{?$/i, depth: 2 },
311
+ { pattern: /^interface\s+\S+\s*\{?$/i, depth: 2 },
312
+ { pattern: /^neighbor\s+\S+\s*\{?$/i, depth: 2 },
313
+ { pattern: /^passive-interface\s+/i, depth: 2 },
314
+ { pattern: /^parameters\s*\{?$/i, depth: 2 },
315
+ { pattern: /^redistribute\s*\{?$/i, depth: 2 },
316
+ { pattern: /^refresh\s*\{?$/i, depth: 2 },
317
+ { pattern: /^timers\s*\{?$/i, depth: 2 },
318
+
319
+ // Inside static routes
320
+ { pattern: /^route\s+\S+\s*\{?$/i, depth: 2 },
321
+ { pattern: /^route6\s+\S+\s*\{?$/i, depth: 2 },
322
+
323
+ // Inside DHCP server
324
+ { pattern: /^shared-network-name\s+\S+\s*\{?$/i, depth: 2 },
325
+ { pattern: /^high-availability\s*\{?$/i, depth: 2 },
326
+ { pattern: /^hostfile-update\s*\{?$/i, depth: 2 },
327
+
328
+ // Inside DNS
329
+ { pattern: /^forwarding\s*\{?$/i, depth: 2 },
330
+ { pattern: /^dynamic\s*\{?$/i, depth: 2 },
331
+
332
+ // Inside IPsec
333
+ { pattern: /^esp-group\s+\S+\s*\{?$/i, depth: 2 },
334
+ { pattern: /^ike-group\s+\S+\s*\{?$/i, depth: 2 },
335
+ { pattern: /^interface\s+\S+\s*\{?$/i, depth: 2 },
336
+ { pattern: /^site-to-site\s*\{?$/i, depth: 2 },
337
+ { pattern: /^profile\s+\S+\s*\{?$/i, depth: 2 },
338
+ { pattern: /^remote-access\s*\{?$/i, depth: 2 },
339
+
340
+ // Inside L2TP/PPTP remote access VPN
341
+ { pattern: /^remote-access\s*\{?$/i, depth: 2 },
342
+
343
+ // Inside VRRP
344
+ { pattern: /^group\s+\S+\s*\{?$/i, depth: 2 },
345
+
346
+ // Inside route-map
347
+ { pattern: /^rule\s+\d+\s*\{?$/i, depth: 2 },
348
+
349
+ // Inside prefix-list
350
+ { pattern: /^rule\s+\d+\s*\{?$/i, depth: 2 },
351
+
352
+ // Inside SSH
353
+ { pattern: /^access-control\s*\{?$/i, depth: 2 },
354
+ { pattern: /^dynamic-protection\s*\{?$/i, depth: 2 },
355
+
356
+ // ============ DEPTH 3: Deeply nested blocks ============
357
+
358
+ // Inside user authentication
359
+ { pattern: /^authentication\s*\{?$/i, depth: 3 },
360
+ { pattern: /^public-keys\s+\S+\s*\{?$/i, depth: 3 },
361
+
362
+ // Inside VIF (VLAN subinterface)
363
+ { pattern: /^vif-c\s+\d+\s*\{?$/i, depth: 3 }, // QinQ inner VLAN
364
+
365
+ // Inside DHCP shared-network
366
+ { pattern: /^subnet\s+\S+\s*\{?$/i, depth: 3 },
367
+
368
+ // Inside BGP address-family
369
+ { pattern: /^ipv4-unicast\s*\{?$/i, depth: 3 },
370
+ { pattern: /^ipv6-unicast\s*\{?$/i, depth: 3 },
371
+ { pattern: /^l2vpn-evpn\s*\{?$/i, depth: 3 },
372
+
373
+ // Inside BGP neighbor
374
+ { pattern: /^address-family\s*\{?$/i, depth: 3 },
375
+
376
+ // Inside OSPF area
377
+ { pattern: /^area-type\s*\{?$/i, depth: 3 },
378
+ { pattern: /^network\s+\S+\s*$/i, depth: 3 },
379
+ { pattern: /^range\s+\S+\s*\{?$/i, depth: 3 },
380
+ { pattern: /^virtual-link\s+\S+\s*\{?$/i, depth: 3 },
381
+
382
+ // Inside static route
383
+ { pattern: /^next-hop\s+\S+\s*\{?$/i, depth: 3 },
384
+ { pattern: /^blackhole\s*\{?$/i, depth: 3 },
385
+
386
+ // Inside IPsec esp-group/ike-group
387
+ { pattern: /^proposal\s+\d+\s*\{?$/i, depth: 3 },
388
+
389
+ // Inside IPsec site-to-site
390
+ { pattern: /^peer\s+\S+\s*\{?$/i, depth: 3 },
391
+
392
+ // Inside firewall rule (match conditions and actions)
393
+ { pattern: /^source\s*\{?$/i, depth: 3 },
394
+ { pattern: /^destination\s*\{?$/i, depth: 3 },
395
+ { pattern: /^state\s*\{?$/i, depth: 3 },
396
+ { pattern: /^tcp\s*\{?$/i, depth: 3 },
397
+ { pattern: /^icmp\s*\{?$/i, depth: 3 },
398
+ { pattern: /^time\s*\{?$/i, depth: 3 },
399
+ { pattern: /^recent\s*\{?$/i, depth: 3 },
400
+ { pattern: /^log\s*\{?$/i, depth: 3 },
401
+ { pattern: /^limit\s*\{?$/i, depth: 3 },
402
+
403
+ // Inside NAT rule
404
+ { pattern: /^source\s*\{?$/i, depth: 3 },
405
+ { pattern: /^destination\s*\{?$/i, depth: 3 },
406
+ { pattern: /^translation\s*\{?$/i, depth: 3 },
407
+
408
+ // Inside zone from
409
+ { pattern: /^firewall\s*\{?$/i, depth: 3 },
410
+
411
+ // Inside route-map rule
412
+ { pattern: /^match\s*\{?$/i, depth: 3 },
413
+ { pattern: /^set\s*\{?$/i, depth: 3 },
414
+ { pattern: /^on-match\s*\{?$/i, depth: 3 },
415
+
416
+ // Inside SNMP
417
+ { pattern: /^community\s+\S+\s*\{?$/i, depth: 3 },
418
+ { pattern: /^trap-target\s+\S+\s*\{?$/i, depth: 3 },
419
+ { pattern: /^v3\s*\{?$/i, depth: 3 },
420
+
421
+ // ============ DEPTH 4: Very deeply nested ============
422
+
423
+ // Inside DHCP subnet
424
+ { pattern: /^static-mapping\s+\S+\s*\{?$/i, depth: 4 },
425
+ { pattern: /^range\s+\d+\s*\{?$/i, depth: 4 },
426
+
427
+ // Inside IPsec peer
428
+ { pattern: /^tunnel\s+\d+\s*\{?$/i, depth: 4 },
429
+ { pattern: /^vti\s*\{?$/i, depth: 4 },
430
+ { pattern: /^authentication\s*\{?$/i, depth: 4 },
431
+ { pattern: /^connection-type\s+/i, depth: 4 },
432
+
433
+ // Inside BGP neighbor address-family
434
+ { pattern: /^ipv4-unicast\s*\{?$/i, depth: 4 },
435
+ { pattern: /^ipv6-unicast\s*\{?$/i, depth: 4 },
436
+
437
+ // Inside SNMPv3
438
+ { pattern: /^user\s+\S+\s*\{?$/i, depth: 4 },
439
+ { pattern: /^group\s+\S+\s*\{?$/i, depth: 4 },
440
+ { pattern: /^view\s+\S+\s*\{?$/i, depth: 4 },
441
+
442
+ // ============ DEPTH 5: Deepest nesting ============
443
+
444
+ // Inside IPsec tunnel
445
+ { pattern: /^local\s*\{?$/i, depth: 5 },
446
+ { pattern: /^remote\s*\{?$/i, depth: 5 },
447
+ { pattern: /^protocol\s*\{?$/i, depth: 5 },
448
+ ],
449
+
450
+ blockEnders: [
451
+ /^\}$/,
452
+ /^\}\s*$/,
453
+ ],
454
+ };
@@ -0,0 +1,72 @@
1
+ // packages/core/src/types/ConfigNode.ts
2
+
3
+ /**
4
+ * Defines the type of a configuration node in the Abstract Syntax Tree (AST).
5
+ * - 'section': Represents a configuration block (e.g., interface, router bgp).
6
+ * - 'command': Represents a single configuration command within a section or globally.
7
+ * - 'comment': Represents a comment line in the configuration.
8
+ * - 'virtual_root': A synthetic node used to wrap orphan commands for rule validation.
9
+ */
10
+ export type NodeType = 'section' | 'command' | 'comment' | 'virtual_root';
11
+
12
+ /**
13
+ * Represents a node in the Abstract Syntax Tree (AST) of a configuration file.
14
+ * This structure normalizes flattened text into a hierarchical tree.
15
+ */
16
+ export interface ConfigNode {
17
+ /**
18
+ * A unique identifier for the node, typically derived from its raw text or path.
19
+ * Example: "interface GigabitEthernet1"
20
+ */
21
+ id: string;
22
+
23
+ /**
24
+ * The type of the configuration node.
25
+ */
26
+ type: NodeType;
27
+
28
+ /**
29
+ * The original raw text line(s) that this node represents.
30
+ */
31
+ rawText: string;
32
+
33
+ /**
34
+ * Parameters extracted from the rawText, typically the command and its arguments.
35
+ * Example: for "interface Gi0/1", params might be ["interface", "Gi0/1"].
36
+ */
37
+ params: string[];
38
+
39
+ /**
40
+ * Child configuration nodes, forming the hierarchical structure.
41
+ */
42
+ children: ConfigNode[];
43
+
44
+ /**
45
+ * Critical for "Snippet Resilience": Indicates if the node originated from
46
+ * the base configuration or a partial snippet.
47
+ */
48
+ source: 'base' | 'snippet';
49
+
50
+ /**
51
+ * Location in the original source file for error reporting and context.
52
+ */
53
+ loc: {
54
+ startLine: number;
55
+ endLine: number;
56
+ };
57
+
58
+ /**
59
+ * The indentation level of the rawText (number of leading whitespace characters).
60
+ * This is crucial for indentation-based parsing.
61
+ */
62
+ indent: number;
63
+
64
+ /**
65
+ * For section nodes, the block depth from BlockStarterDefs.
66
+ * - 0: Top-level blocks (interface, router, vlan)
67
+ * - 1: Nested blocks (address-family inside router)
68
+ * - 2: Deeply nested (vrf inside address-family)
69
+ * - undefined: For non-section nodes (commands, comments)
70
+ */
71
+ blockDepth?: number;
72
+ }
@@ -0,0 +1,158 @@
1
+ // packages/core/src/types/DeclarativeRule.ts
2
+
3
+ /**
4
+ * SEC-001: Declarative Rule Types
5
+ *
6
+ * Provides a declarative rule format that can be evaluated without
7
+ * executing arbitrary JavaScript code. This is the preferred format
8
+ * for external/untrusted rules as it executes natively with zero overhead.
9
+ */
10
+
11
+ import type { RuleVendor, RuleMetadata } from './IRule';
12
+
13
+ /**
14
+ * Declarative check conditions that can be evaluated safely.
15
+ * These map to common pattern matching and node inspection operations.
16
+ */
17
+ export type DeclarativeCheck =
18
+ // Pattern matching
19
+ | { type: 'match'; pattern: string; flags?: string }
20
+ | { type: 'not_match'; pattern: string; flags?: string }
21
+
22
+ // Text contains
23
+ | { type: 'contains'; text: string }
24
+ | { type: 'not_contains'; text: string }
25
+
26
+ // Child node existence
27
+ | { type: 'child_exists'; selector: string }
28
+ | { type: 'child_not_exists'; selector: string }
29
+
30
+ // Child text matching
31
+ | { type: 'child_matches'; selector: string; pattern: string; flags?: string }
32
+ | { type: 'child_contains'; selector: string; text: string }
33
+
34
+ // Logical combinators
35
+ | { type: 'and'; conditions: DeclarativeCheck[] }
36
+ | { type: 'or'; conditions: DeclarativeCheck[] }
37
+ | { type: 'not'; condition: DeclarativeCheck }
38
+
39
+ // SEC-001: Custom code (sandboxed execution)
40
+ // Use sparingly - only when declarative checks are insufficient
41
+ | { type: 'custom'; code: string };
42
+
43
+ /**
44
+ * A declarative rule definition that can be safely evaluated.
45
+ *
46
+ * Unlike IRule with its JavaScript check function, DeclarativeRule
47
+ * uses a JSON-serializable check condition that can be evaluated
48
+ * without running arbitrary code.
49
+ */
50
+ export interface DeclarativeRule {
51
+ /** Unique rule identifier */
52
+ id: string;
53
+
54
+ /** Optional selector for node filtering */
55
+ selector?: string;
56
+
57
+ /** Optional vendor(s) this rule applies to */
58
+ vendor?: RuleVendor | RuleVendor[];
59
+
60
+ /** Rule metadata */
61
+ metadata: RuleMetadata;
62
+
63
+ /** The declarative check condition */
64
+ check: DeclarativeCheck;
65
+ }
66
+
67
+ /**
68
+ * Type guard to check if an object is a valid DeclarativeCheck.
69
+ */
70
+ export function isDeclarativeCheck(obj: unknown): obj is DeclarativeCheck {
71
+ if (typeof obj !== 'object' || obj === null) {
72
+ return false;
73
+ }
74
+
75
+ const check = obj as Record<string, unknown>;
76
+
77
+ switch (check.type) {
78
+ case 'match':
79
+ case 'not_match':
80
+ return typeof check.pattern === 'string' &&
81
+ (check.flags === undefined || typeof check.flags === 'string');
82
+
83
+ case 'contains':
84
+ case 'not_contains':
85
+ return typeof check.text === 'string';
86
+
87
+ case 'child_exists':
88
+ case 'child_not_exists':
89
+ return typeof check.selector === 'string';
90
+
91
+ case 'child_matches':
92
+ return typeof check.selector === 'string' &&
93
+ typeof check.pattern === 'string' &&
94
+ (check.flags === undefined || typeof check.flags === 'string');
95
+
96
+ case 'child_contains':
97
+ return typeof check.selector === 'string' &&
98
+ typeof check.text === 'string';
99
+
100
+ case 'and':
101
+ case 'or':
102
+ return Array.isArray(check.conditions) &&
103
+ check.conditions.every(isDeclarativeCheck);
104
+
105
+ case 'not':
106
+ return isDeclarativeCheck(check.condition);
107
+
108
+ case 'custom':
109
+ return typeof check.code === 'string';
110
+
111
+ default:
112
+ return false;
113
+ }
114
+ }
115
+
116
+ /**
117
+ * Type guard to check if an object is a valid DeclarativeRule.
118
+ */
119
+ export function isDeclarativeRule(obj: unknown): obj is DeclarativeRule {
120
+ if (typeof obj !== 'object' || obj === null) {
121
+ return false;
122
+ }
123
+
124
+ const rule = obj as Record<string, unknown>;
125
+
126
+ // Check required fields
127
+ if (typeof rule.id !== 'string' || rule.id.length === 0) {
128
+ return false;
129
+ }
130
+
131
+ // Check optional selector
132
+ if (rule.selector !== undefined && typeof rule.selector !== 'string') {
133
+ return false;
134
+ }
135
+
136
+ // Check metadata
137
+ if (typeof rule.metadata !== 'object' || rule.metadata === null) {
138
+ return false;
139
+ }
140
+
141
+ const metadata = rule.metadata as Record<string, unknown>;
142
+ if (!['error', 'warning', 'info'].includes(metadata.level as string)) {
143
+ return false;
144
+ }
145
+ if (typeof metadata.obu !== 'string') {
146
+ return false;
147
+ }
148
+ if (typeof metadata.owner !== 'string') {
149
+ return false;
150
+ }
151
+
152
+ // Check declarative check
153
+ if (!isDeclarativeCheck(rule.check)) {
154
+ return false;
155
+ }
156
+
157
+ return true;
158
+ }