@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,722 @@
1
+ // packages/rule-helpers/src/mikrotik/helpers.ts
2
+ // MikroTik RouterOS-specific helper functions
3
+
4
+ import type { ConfigNode } from '../../types/ConfigNode';
5
+ import { parseIp, prefixToMask } from '../common/helpers';
6
+
7
+ /**
8
+ * Check if a MikroTik resource is disabled (has disabled=yes property)
9
+ */
10
+ export const isDisabledResource = (nodeOrCommand: ConfigNode | string): boolean => {
11
+ const id = typeof nodeOrCommand === 'string' ? nodeOrCommand : nodeOrCommand.id;
12
+ const lower = id.toLowerCase();
13
+ return (
14
+ /\bdisabled=yes\b/i.test(lower) ||
15
+ /\bdisabled="?yes"?\b/i.test(lower)
16
+ );
17
+ };
18
+
19
+ /**
20
+ * Check if interface is a physical ethernet port (ether1, ether2, etc.)
21
+ */
22
+ export const isPhysicalInterface = (interfaceName: string): boolean => {
23
+ const name = interfaceName.toLowerCase();
24
+ // MikroTik physical ethernet interfaces: ether1, ether2, etc.
25
+ // Also match sfp1, sfp-sfpplus1, combo1, etc.
26
+ return /^ether\d+$/.test(name) ||
27
+ /^sfp\d+$/.test(name) ||
28
+ /^sfp-sfpplus\d+$/.test(name) ||
29
+ /^combo\d+$/.test(name) ||
30
+ /^qsfp\d+$/.test(name);
31
+ };
32
+
33
+ /**
34
+ * Check if interface is a loopback
35
+ */
36
+ export const isLoopback = (interfaceName: string): boolean => {
37
+ const name = interfaceName.toLowerCase();
38
+ return name === 'lo' || name.startsWith('loopback');
39
+ };
40
+
41
+ /**
42
+ * Check if interface is a bridge
43
+ */
44
+ export const isBridgeInterface = (interfaceName: string): boolean => {
45
+ const name = interfaceName.toLowerCase();
46
+ return name.startsWith('bridge') || /^br\d+$/.test(name);
47
+ };
48
+
49
+ /**
50
+ * Check if interface is a VLAN
51
+ */
52
+ export const isVlanInterface = (interfaceName: string): boolean => {
53
+ const name = interfaceName.toLowerCase();
54
+ return name.startsWith('vlan') || /^vlan\d+$/.test(name);
55
+ };
56
+
57
+ /**
58
+ * Check if interface is a bonding (LAG)
59
+ */
60
+ export const isBondingInterface = (interfaceName: string): boolean => {
61
+ const name = interfaceName.toLowerCase();
62
+ return name.startsWith('bonding') || /^bond\d+$/.test(name);
63
+ };
64
+
65
+ /**
66
+ * Check if interface is WireGuard
67
+ */
68
+ export const isWireGuardInterface = (interfaceName: string): boolean => {
69
+ const name = interfaceName.toLowerCase();
70
+ return name.startsWith('wireguard') || /^wg\d+$/.test(name);
71
+ };
72
+
73
+ /**
74
+ * Check if interface is a tunnel type
75
+ */
76
+ export const isTunnelInterface = (interfaceName: string): boolean => {
77
+ const name = interfaceName.toLowerCase();
78
+ return (
79
+ name.startsWith('eoip') ||
80
+ name.startsWith('gre') ||
81
+ name.startsWith('ipip') ||
82
+ name.startsWith('vxlan') ||
83
+ name.startsWith('l2tp') ||
84
+ name.startsWith('pptp') ||
85
+ name.startsWith('sstp') ||
86
+ name.startsWith('ovpn') ||
87
+ name.startsWith('pppoe')
88
+ );
89
+ };
90
+
91
+ /**
92
+ * Parse a MikroTik property value from a command string
93
+ * Example: parseProperty("add address=192.168.1.1/24 interface=LAN", "address") returns "192.168.1.1/24"
94
+ */
95
+ export const parseProperty = (commandStr: string, propertyName: string): string | undefined => {
96
+ // Match property=value or property="value" or property='value'
97
+ const regex = new RegExp(`\\b${propertyName}=(?:"([^"]+)"|'([^']+)'|(\\S+))`, 'i');
98
+ const match = commandStr.match(regex);
99
+ if (match) {
100
+ return match[1] || match[2] || match[3];
101
+ }
102
+ return undefined;
103
+ };
104
+
105
+ /**
106
+ * Check if a command/node has a specific property
107
+ */
108
+ export const hasProperty = (nodeOrCommand: ConfigNode | string, propertyName: string): boolean => {
109
+ const str = typeof nodeOrCommand === 'string' ? nodeOrCommand : nodeOrCommand.id;
110
+ const regex = new RegExp(`\\b${propertyName}=`, 'i');
111
+ return regex.test(str);
112
+ };
113
+
114
+ /**
115
+ * Get the firewall chain from a firewall rule command
116
+ */
117
+ export const getFirewallChain = (nodeOrCommand: ConfigNode | string): string | undefined => {
118
+ const str = typeof nodeOrCommand === 'string' ? nodeOrCommand : nodeOrCommand.id;
119
+ return parseProperty(str, 'chain');
120
+ };
121
+
122
+ /**
123
+ * Get the firewall action from a firewall rule command
124
+ */
125
+ export const getFirewallAction = (nodeOrCommand: ConfigNode | string): string | undefined => {
126
+ const str = typeof nodeOrCommand === 'string' ? nodeOrCommand : nodeOrCommand.id;
127
+ return parseProperty(str, 'action');
128
+ };
129
+
130
+ /**
131
+ * Get the interface from a command
132
+ */
133
+ export const getInterface = (nodeOrCommand: ConfigNode | string): string | undefined => {
134
+ const str = typeof nodeOrCommand === 'string' ? nodeOrCommand : nodeOrCommand.id;
135
+ // Try 'interface=' first, then 'in-interface=' or 'out-interface='
136
+ return parseProperty(str, 'interface') ||
137
+ parseProperty(str, 'in-interface') ||
138
+ parseProperty(str, 'out-interface');
139
+ };
140
+
141
+ /**
142
+ * Get the comment from a command
143
+ */
144
+ export const getComment = (nodeOrCommand: ConfigNode | string): string | undefined => {
145
+ const str = typeof nodeOrCommand === 'string' ? nodeOrCommand : nodeOrCommand.id;
146
+ return parseProperty(str, 'comment');
147
+ };
148
+
149
+ /**
150
+ * Get the name property from a command
151
+ */
152
+ export const getName = (nodeOrCommand: ConfigNode | string): string | undefined => {
153
+ const str = typeof nodeOrCommand === 'string' ? nodeOrCommand : nodeOrCommand.id;
154
+ return parseProperty(str, 'name');
155
+ };
156
+
157
+ /**
158
+ * Check if a command is an 'add' command
159
+ */
160
+ export const isAddCommand = (nodeOrCommand: ConfigNode | string): boolean => {
161
+ const str = typeof nodeOrCommand === 'string' ? nodeOrCommand : nodeOrCommand.id;
162
+ return /^add\s+/i.test(str.trim());
163
+ };
164
+
165
+ /**
166
+ * Check if a command is a 'set' command
167
+ */
168
+ export const isSetCommand = (nodeOrCommand: ConfigNode | string): boolean => {
169
+ const str = typeof nodeOrCommand === 'string' ? nodeOrCommand : nodeOrCommand.id;
170
+ return /^set\s+/i.test(str.trim());
171
+ };
172
+
173
+ /**
174
+ * Get all 'add' commands from a node's children
175
+ */
176
+ export const getAddCommands = (node: ConfigNode): ConfigNode[] => {
177
+ return node.children.filter((child) => isAddCommand(child));
178
+ };
179
+
180
+ /**
181
+ * Get all 'set' commands from a node's children
182
+ */
183
+ export const getSetCommands = (node: ConfigNode): ConfigNode[] => {
184
+ return node.children.filter((child) => isSetCommand(child));
185
+ };
186
+
187
+ /**
188
+ * Check if a path block matches a specific path
189
+ * Example: isPathBlock(node, '/ip firewall filter') checks if node.id matches
190
+ */
191
+ export const isPathBlock = (node: ConfigNode, path: string): boolean => {
192
+ const nodeId = node.id.toLowerCase().trim();
193
+ const targetPath = path.toLowerCase().trim();
194
+ return nodeId === targetPath || nodeId.startsWith(targetPath + ' ');
195
+ };
196
+
197
+ /**
198
+ * Find a child node that matches a path pattern
199
+ */
200
+ export const findPathBlock = (node: ConfigNode, pathPrefix: string): ConfigNode | undefined => {
201
+ return node.children.find((child) =>
202
+ child.id.toLowerCase().trim().startsWith(pathPrefix.toLowerCase())
203
+ );
204
+ };
205
+
206
+ /**
207
+ * Find all child nodes that match a path pattern
208
+ */
209
+ export const findPathBlocks = (node: ConfigNode, pathPrefix: string): ConfigNode[] => {
210
+ return node.children.filter((child) =>
211
+ child.id.toLowerCase().trim().startsWith(pathPrefix.toLowerCase())
212
+ );
213
+ };
214
+
215
+ /**
216
+ * Parse MikroTik address format (e.g., "192.168.1.1/24")
217
+ * @param address The address string with CIDR notation
218
+ * @returns Object with ip number, prefix length, and mask, or null if invalid
219
+ */
220
+ export const parseMikroTikAddress = (
221
+ address: string
222
+ ): { ip: number; prefix: number; mask: number } | null => {
223
+ // Remove quotes if present
224
+ const cleanAddress = address.replace(/['"]/g, '');
225
+ const parts = cleanAddress.split('/');
226
+ if (parts.length !== 2) return null;
227
+
228
+ const ipPart = parts[0];
229
+ const prefixPart = parts[1];
230
+ if (!ipPart || !prefixPart) return null;
231
+
232
+ const ip = parseIp(ipPart);
233
+ const prefix = parseInt(prefixPart, 10);
234
+
235
+ if (ip === null || isNaN(prefix) || prefix < 0 || prefix > 32) {
236
+ return null;
237
+ }
238
+
239
+ return {
240
+ ip,
241
+ prefix,
242
+ mask: prefixToMask(prefix),
243
+ };
244
+ };
245
+
246
+ /**
247
+ * Get connection states from a firewall rule
248
+ * Example: "connection-state=established,related" returns ['established', 'related']
249
+ */
250
+ export const getConnectionStates = (nodeOrCommand: ConfigNode | string): string[] => {
251
+ const str = typeof nodeOrCommand === 'string' ? nodeOrCommand : nodeOrCommand.id;
252
+ const states = parseProperty(str, 'connection-state');
253
+ if (!states) return [];
254
+ return states.split(',').map((s) => s.trim().toLowerCase());
255
+ };
256
+
257
+ /**
258
+ * Check if a firewall rule has stateful tracking (established,related)
259
+ */
260
+ export const hasStatefulTracking = (nodeOrCommand: ConfigNode | string): boolean => {
261
+ const states = getConnectionStates(nodeOrCommand);
262
+ return states.includes('established') || states.includes('related');
263
+ };
264
+
265
+ /**
266
+ * Get service port from /ip service command
267
+ */
268
+ export const getServicePort = (nodeOrCommand: ConfigNode | string): number | undefined => {
269
+ const str = typeof nodeOrCommand === 'string' ? nodeOrCommand : nodeOrCommand.id;
270
+ const port = parseProperty(str, 'port');
271
+ if (port) {
272
+ const parsed = parseInt(port, 10);
273
+ return isNaN(parsed) ? undefined : parsed;
274
+ }
275
+ return undefined;
276
+ };
277
+
278
+ /**
279
+ * Check if a service is disabled
280
+ */
281
+ export const isServiceDisabled = (nodeOrCommand: ConfigNode | string): boolean => {
282
+ const str = typeof nodeOrCommand === 'string' ? nodeOrCommand : nodeOrCommand.id;
283
+ const disabled = parseProperty(str, 'disabled');
284
+ return disabled?.toLowerCase() === 'yes';
285
+ };
286
+
287
+ /**
288
+ * Get all firewall rules from a firewall path block
289
+ */
290
+ export const getFirewallRules = (firewallNode: ConfigNode): ConfigNode[] => {
291
+ return firewallNode.children.filter((child) => isAddCommand(child));
292
+ };
293
+
294
+ /**
295
+ * Get NAT type from a NAT rule
296
+ */
297
+ export const getNatAction = (nodeOrCommand: ConfigNode | string): string | undefined => {
298
+ const str = typeof nodeOrCommand === 'string' ? nodeOrCommand : nodeOrCommand.id;
299
+ return parseProperty(str, 'action');
300
+ };
301
+
302
+ /**
303
+ * Get out-interface from a NAT rule
304
+ */
305
+ export const getOutInterface = (nodeOrCommand: ConfigNode | string): string | undefined => {
306
+ const str = typeof nodeOrCommand === 'string' ? nodeOrCommand : nodeOrCommand.id;
307
+ return parseProperty(str, 'out-interface');
308
+ };
309
+
310
+ /**
311
+ * Get in-interface from a rule
312
+ */
313
+ export const getInInterface = (nodeOrCommand: ConfigNode | string): string | undefined => {
314
+ const str = typeof nodeOrCommand === 'string' ? nodeOrCommand : nodeOrCommand.id;
315
+ return parseProperty(str, 'in-interface');
316
+ };
317
+
318
+ /**
319
+ * Check if identity (hostname) is configured in a system identity block
320
+ */
321
+ export const getSystemIdentity = (node: ConfigNode): string | undefined => {
322
+ // Look for "set name=..." in /system identity
323
+ for (const child of node.children) {
324
+ if (isSetCommand(child)) {
325
+ const name = getName(child);
326
+ if (name) return name;
327
+ }
328
+ }
329
+ return undefined;
330
+ };
331
+
332
+ /**
333
+ * Check if NTP client is enabled
334
+ */
335
+ export const isNtpEnabled = (nodeOrCommand: ConfigNode | string): boolean => {
336
+ const str = typeof nodeOrCommand === 'string' ? nodeOrCommand : nodeOrCommand.id;
337
+ const enabled = parseProperty(str, 'enabled');
338
+ return enabled?.toLowerCase() === 'yes';
339
+ };
340
+
341
+ /**
342
+ * Get NTP servers from /system ntp client servers block
343
+ */
344
+ export const getNtpServers = (ntpNode: ConfigNode): string[] => {
345
+ const servers: string[] = [];
346
+ for (const child of ntpNode.children) {
347
+ if (isAddCommand(child)) {
348
+ const address = parseProperty(child.id, 'address');
349
+ if (address) servers.push(address);
350
+ }
351
+ }
352
+ return servers;
353
+ };
354
+
355
+ /**
356
+ * Check if SSH strong-crypto is enabled
357
+ */
358
+ export const isSshStrongCrypto = (nodeOrCommand: ConfigNode | string): boolean => {
359
+ const str = typeof nodeOrCommand === 'string' ? nodeOrCommand : nodeOrCommand.id;
360
+ const strongCrypto = parseProperty(str, 'strong-crypto');
361
+ return strongCrypto?.toLowerCase() === 'yes';
362
+ };
363
+
364
+ /**
365
+ * Get SSH host key type
366
+ */
367
+ export const getSshHostKeyType = (nodeOrCommand: ConfigNode | string): string | undefined => {
368
+ const str = typeof nodeOrCommand === 'string' ? nodeOrCommand : nodeOrCommand.id;
369
+ return parseProperty(str, 'host-key-type');
370
+ };
371
+
372
+ /**
373
+ * Get SNMP community security level
374
+ */
375
+ export const getSnmpSecurity = (nodeOrCommand: ConfigNode | string): string | undefined => {
376
+ const str = typeof nodeOrCommand === 'string' ? nodeOrCommand : nodeOrCommand.id;
377
+ return parseProperty(str, 'security');
378
+ };
379
+
380
+ /**
381
+ * Get SNMP community name
382
+ */
383
+ export const getSnmpCommunityName = (nodeOrCommand: ConfigNode | string): string | undefined => {
384
+ const str = typeof nodeOrCommand === 'string' ? nodeOrCommand : nodeOrCommand.id;
385
+ return parseProperty(str, 'name');
386
+ };
387
+
388
+ /**
389
+ * Check if SNMP has authentication protocol configured
390
+ */
391
+ export const hasSnmpAuthProtocol = (nodeOrCommand: ConfigNode | string): boolean => {
392
+ const str = typeof nodeOrCommand === 'string' ? nodeOrCommand : nodeOrCommand.id;
393
+ return hasProperty(str, 'authentication-protocol');
394
+ };
395
+
396
+ /**
397
+ * Check if SNMP has encryption protocol configured
398
+ */
399
+ export const hasSnmpEncryptionProtocol = (nodeOrCommand: ConfigNode | string): boolean => {
400
+ const str = typeof nodeOrCommand === 'string' ? nodeOrCommand : nodeOrCommand.id;
401
+ return hasProperty(str, 'encryption-protocol');
402
+ };
403
+
404
+ /**
405
+ * Get allowed interface list property
406
+ */
407
+ export const getAllowedInterfaceList = (nodeOrCommand: ConfigNode | string): string | undefined => {
408
+ const str = typeof nodeOrCommand === 'string' ? nodeOrCommand : nodeOrCommand.id;
409
+ return parseProperty(str, 'allowed-interface-list');
410
+ };
411
+
412
+ /**
413
+ * Get discover interface list from neighbor discovery settings
414
+ */
415
+ export const getDiscoverInterfaceList = (nodeOrCommand: ConfigNode | string): string | undefined => {
416
+ const str = typeof nodeOrCommand === 'string' ? nodeOrCommand : nodeOrCommand.id;
417
+ return parseProperty(str, 'discover-interface-list');
418
+ };
419
+
420
+ /**
421
+ * Check if a feature is enabled (common pattern)
422
+ */
423
+ export const isEnabled = (nodeOrCommand: ConfigNode | string): boolean => {
424
+ const str = typeof nodeOrCommand === 'string' ? nodeOrCommand : nodeOrCommand.id;
425
+ const enabled = parseProperty(str, 'enabled');
426
+ return enabled?.toLowerCase() === 'yes';
427
+ };
428
+
429
+ /**
430
+ * Check if MAC-Ping is enabled
431
+ */
432
+ export const isMacPingEnabled = (nodeOrCommand: ConfigNode | string): boolean => {
433
+ const str = typeof nodeOrCommand === 'string' ? nodeOrCommand : nodeOrCommand.id;
434
+ const enabled = parseProperty(str, 'enabled');
435
+ // Default is enabled if not explicitly disabled
436
+ return enabled?.toLowerCase() !== 'no';
437
+ };
438
+
439
+ /**
440
+ * Get BGP TCP-MD5 key (checks if authentication is configured)
441
+ */
442
+ export const getBgpTcpMd5Key = (nodeOrCommand: ConfigNode | string): string | undefined => {
443
+ const str = typeof nodeOrCommand === 'string' ? nodeOrCommand : nodeOrCommand.id;
444
+ return parseProperty(str, 'tcp-md5-key');
445
+ };
446
+
447
+ /**
448
+ * Get BGP remote AS
449
+ */
450
+ export const getBgpRemoteAs = (nodeOrCommand: ConfigNode | string): string | undefined => {
451
+ const str = typeof nodeOrCommand === 'string' ? nodeOrCommand : nodeOrCommand.id;
452
+ // RouterOS 7 uses remote.as, RouterOS 6 uses remote-as
453
+ return parseProperty(str, 'remote.as') || parseProperty(str, 'remote-as');
454
+ };
455
+
456
+ /**
457
+ * Get BGP max prefix limit
458
+ */
459
+ export const getBgpMaxPrefixLimit = (nodeOrCommand: ConfigNode | string): number | undefined => {
460
+ const str = typeof nodeOrCommand === 'string' ? nodeOrCommand : nodeOrCommand.id;
461
+ // RouterOS 7: input.limit-process-routes-ipv4
462
+ // RouterOS 6: max-prefix-limit
463
+ const limit = parseProperty(str, 'input.limit-process-routes-ipv4') ||
464
+ parseProperty(str, 'max-prefix-limit');
465
+ if (limit) {
466
+ const parsed = parseInt(limit, 10);
467
+ return isNaN(parsed) ? undefined : parsed;
468
+ }
469
+ return undefined;
470
+ };
471
+
472
+ /**
473
+ * Check if BGP has input filter
474
+ */
475
+ export const hasBgpInputFilter = (nodeOrCommand: ConfigNode | string): boolean => {
476
+ const str = typeof nodeOrCommand === 'string' ? nodeOrCommand : nodeOrCommand.id;
477
+ // RouterOS 7: input.filter
478
+ // RouterOS 6: in-filter
479
+ return hasProperty(str, 'input.filter') || hasProperty(str, 'in-filter');
480
+ };
481
+
482
+ /**
483
+ * Check if BGP has output filter
484
+ */
485
+ export const hasBgpOutputFilter = (nodeOrCommand: ConfigNode | string): boolean => {
486
+ const str = typeof nodeOrCommand === 'string' ? nodeOrCommand : nodeOrCommand.id;
487
+ // RouterOS 7: output.filter
488
+ // RouterOS 6: out-filter
489
+ return hasProperty(str, 'output.filter') || hasProperty(str, 'out-filter');
490
+ };
491
+
492
+ /**
493
+ * Get OSPF authentication type
494
+ */
495
+ export const getOspfAuth = (nodeOrCommand: ConfigNode | string): string | undefined => {
496
+ const str = typeof nodeOrCommand === 'string' ? nodeOrCommand : nodeOrCommand.id;
497
+ return parseProperty(str, 'auth');
498
+ };
499
+
500
+ /**
501
+ * Get OSPF authentication key
502
+ */
503
+ export const getOspfAuthKey = (nodeOrCommand: ConfigNode | string): string | undefined => {
504
+ const str = typeof nodeOrCommand === 'string' ? nodeOrCommand : nodeOrCommand.id;
505
+ return parseProperty(str, 'auth-key') || parseProperty(str, 'authentication-key');
506
+ };
507
+
508
+ /**
509
+ * Get VRRP authentication type
510
+ */
511
+ export const getVrrpAuth = (nodeOrCommand: ConfigNode | string): string | undefined => {
512
+ const str = typeof nodeOrCommand === 'string' ? nodeOrCommand : nodeOrCommand.id;
513
+ return parseProperty(str, 'authentication');
514
+ };
515
+
516
+ /**
517
+ * Get VRRP password
518
+ */
519
+ export const getVrrpPassword = (nodeOrCommand: ConfigNode | string): string | undefined => {
520
+ const str = typeof nodeOrCommand === 'string' ? nodeOrCommand : nodeOrCommand.id;
521
+ return parseProperty(str, 'password');
522
+ };
523
+
524
+ /**
525
+ * Get IPsec encryption algorithm
526
+ */
527
+ export const getIpsecEncAlgorithm = (nodeOrCommand: ConfigNode | string): string | undefined => {
528
+ const str = typeof nodeOrCommand === 'string' ? nodeOrCommand : nodeOrCommand.id;
529
+ return parseProperty(str, 'enc-algorithm') || parseProperty(str, 'enc-algorithms');
530
+ };
531
+
532
+ /**
533
+ * Get IPsec hash algorithm
534
+ */
535
+ export const getIpsecHashAlgorithm = (nodeOrCommand: ConfigNode | string): string | undefined => {
536
+ const str = typeof nodeOrCommand === 'string' ? nodeOrCommand : nodeOrCommand.id;
537
+ return parseProperty(str, 'hash-algorithm') || parseProperty(str, 'auth-algorithms');
538
+ };
539
+
540
+ /**
541
+ * Get IPsec DH group
542
+ */
543
+ export const getIpsecDhGroup = (nodeOrCommand: ConfigNode | string): string | undefined => {
544
+ const str = typeof nodeOrCommand === 'string' ? nodeOrCommand : nodeOrCommand.id;
545
+ return parseProperty(str, 'dh-group') || parseProperty(str, 'pfs-group');
546
+ };
547
+
548
+ /**
549
+ * Check if bridge has VLAN filtering enabled
550
+ */
551
+ export const hasBridgeVlanFiltering = (nodeOrCommand: ConfigNode | string): boolean => {
552
+ const str = typeof nodeOrCommand === 'string' ? nodeOrCommand : nodeOrCommand.id;
553
+ const vlanFiltering = parseProperty(str, 'vlan-filtering');
554
+ return vlanFiltering?.toLowerCase() === 'yes';
555
+ };
556
+
557
+ /**
558
+ * Get bridge frame types
559
+ */
560
+ export const getBridgeFrameTypes = (nodeOrCommand: ConfigNode | string): string | undefined => {
561
+ const str = typeof nodeOrCommand === 'string' ? nodeOrCommand : nodeOrCommand.id;
562
+ return parseProperty(str, 'frame-types');
563
+ };
564
+
565
+ /**
566
+ * Check if syslog (remote logging) is configured
567
+ */
568
+ export const getSyslogTarget = (nodeOrCommand: ConfigNode | string): string | undefined => {
569
+ const str = typeof nodeOrCommand === 'string' ? nodeOrCommand : nodeOrCommand.id;
570
+ return parseProperty(str, 'target');
571
+ };
572
+
573
+ /**
574
+ * Get syslog remote address
575
+ */
576
+ export const getSyslogRemote = (nodeOrCommand: ConfigNode | string): string | undefined => {
577
+ const str = typeof nodeOrCommand === 'string' ? nodeOrCommand : nodeOrCommand.id;
578
+ return parseProperty(str, 'remote');
579
+ };
580
+
581
+ /**
582
+ * Get address list name from a rule
583
+ */
584
+ export const getAddressList = (nodeOrCommand: ConfigNode | string): string | undefined => {
585
+ const str = typeof nodeOrCommand === 'string' ? nodeOrCommand : nodeOrCommand.id;
586
+ return parseProperty(str, 'address-list');
587
+ };
588
+
589
+ /**
590
+ * Get source address list from a rule
591
+ */
592
+ export const getSrcAddressList = (nodeOrCommand: ConfigNode | string): string | undefined => {
593
+ const str = typeof nodeOrCommand === 'string' ? nodeOrCommand : nodeOrCommand.id;
594
+ return parseProperty(str, 'src-address-list');
595
+ };
596
+
597
+ /**
598
+ * Get destination address list from a rule
599
+ */
600
+ export const getDstAddressList = (nodeOrCommand: ConfigNode | string): string | undefined => {
601
+ const str = typeof nodeOrCommand === 'string' ? nodeOrCommand : nodeOrCommand.id;
602
+ return parseProperty(str, 'dst-address-list');
603
+ };
604
+
605
+ /**
606
+ * Check if firewall rule has logging enabled
607
+ */
608
+ export const hasFirewallLogging = (nodeOrCommand: ConfigNode | string): boolean => {
609
+ const str = typeof nodeOrCommand === 'string' ? nodeOrCommand : nodeOrCommand.id;
610
+ const log = parseProperty(str, 'log');
611
+ return log?.toLowerCase() === 'yes';
612
+ };
613
+
614
+ /**
615
+ * Get log prefix from a firewall rule
616
+ */
617
+ export const getLogPrefix = (nodeOrCommand: ConfigNode | string): string | undefined => {
618
+ const str = typeof nodeOrCommand === 'string' ? nodeOrCommand : nodeOrCommand.id;
619
+ return parseProperty(str, 'log-prefix');
620
+ };
621
+
622
+ /**
623
+ * Get connection limit from a rule
624
+ */
625
+ export const getConnectionLimit = (nodeOrCommand: ConfigNode | string): string | undefined => {
626
+ const str = typeof nodeOrCommand === 'string' ? nodeOrCommand : nodeOrCommand.id;
627
+ return parseProperty(str, 'connection-limit');
628
+ };
629
+
630
+ /**
631
+ * Get limit (rate limit) from a rule
632
+ */
633
+ export const getRateLimit = (nodeOrCommand: ConfigNode | string): string | undefined => {
634
+ const str = typeof nodeOrCommand === 'string' ? nodeOrCommand : nodeOrCommand.id;
635
+ return parseProperty(str, 'limit');
636
+ };
637
+
638
+ /**
639
+ * Get TCP flags from a firewall rule
640
+ */
641
+ export const getTcpFlags = (nodeOrCommand: ConfigNode | string): string | undefined => {
642
+ const str = typeof nodeOrCommand === 'string' ? nodeOrCommand : nodeOrCommand.id;
643
+ return parseProperty(str, 'tcp-flags');
644
+ };
645
+
646
+ /**
647
+ * Get destination port from a rule
648
+ */
649
+ export const getDstPort = (nodeOrCommand: ConfigNode | string): string | undefined => {
650
+ const str = typeof nodeOrCommand === 'string' ? nodeOrCommand : nodeOrCommand.id;
651
+ return parseProperty(str, 'dst-port');
652
+ };
653
+
654
+ /**
655
+ * Get protocol from a rule
656
+ */
657
+ export const getProtocol = (nodeOrCommand: ConfigNode | string): string | undefined => {
658
+ const str = typeof nodeOrCommand === 'string' ? nodeOrCommand : nodeOrCommand.id;
659
+ return parseProperty(str, 'protocol');
660
+ };
661
+
662
+ /**
663
+ * Check if IP cloud DDNS is enabled
664
+ */
665
+ export const isCloudDdnsEnabled = (nodeOrCommand: ConfigNode | string): boolean => {
666
+ const str = typeof nodeOrCommand === 'string' ? nodeOrCommand : nodeOrCommand.id;
667
+ const ddnsEnabled = parseProperty(str, 'ddns-enabled');
668
+ return ddnsEnabled?.toLowerCase() === 'yes';
669
+ };
670
+
671
+ /**
672
+ * Check if IP proxy is enabled
673
+ */
674
+ export const isProxyEnabled = (nodeOrCommand: ConfigNode | string): boolean => {
675
+ const str = typeof nodeOrCommand === 'string' ? nodeOrCommand : nodeOrCommand.id;
676
+ const enabled = parseProperty(str, 'enabled');
677
+ return enabled?.toLowerCase() === 'yes';
678
+ };
679
+
680
+ /**
681
+ * Check if IP SOCKS is enabled
682
+ */
683
+ export const isSocksEnabled = (nodeOrCommand: ConfigNode | string): boolean => {
684
+ const str = typeof nodeOrCommand === 'string' ? nodeOrCommand : nodeOrCommand.id;
685
+ const enabled = parseProperty(str, 'enabled');
686
+ return enabled?.toLowerCase() === 'yes';
687
+ };
688
+
689
+ /**
690
+ * Check if UPnP is enabled
691
+ */
692
+ export const isUpnpEnabled = (nodeOrCommand: ConfigNode | string): boolean => {
693
+ const str = typeof nodeOrCommand === 'string' ? nodeOrCommand : nodeOrCommand.id;
694
+ const enabled = parseProperty(str, 'enabled');
695
+ return enabled?.toLowerCase() === 'yes';
696
+ };
697
+
698
+ /**
699
+ * Check if DNS allows remote requests
700
+ */
701
+ export const isDnsAllowRemoteRequests = (nodeOrCommand: ConfigNode | string): boolean => {
702
+ const str = typeof nodeOrCommand === 'string' ? nodeOrCommand : nodeOrCommand.id;
703
+ const allowRemote = parseProperty(str, 'allow-remote-requests');
704
+ return allowRemote?.toLowerCase() === 'yes';
705
+ };
706
+
707
+ /**
708
+ * Get system note content
709
+ */
710
+ export const getSystemNote = (nodeOrCommand: ConfigNode | string): string | undefined => {
711
+ const str = typeof nodeOrCommand === 'string' ? nodeOrCommand : nodeOrCommand.id;
712
+ return parseProperty(str, 'note');
713
+ };
714
+
715
+ /**
716
+ * Check if system note is shown at login
717
+ */
718
+ export const isNoteShowAtLogin = (nodeOrCommand: ConfigNode | string): boolean => {
719
+ const str = typeof nodeOrCommand === 'string' ? nodeOrCommand : nodeOrCommand.id;
720
+ const showAtLogin = parseProperty(str, 'show-at-login');
721
+ return showAtLogin?.toLowerCase() === 'yes';
722
+ };