@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.
- package/LICENSE +190 -0
- package/README.md +86 -0
- package/package.json +60 -0
- package/src/constants.ts +77 -0
- package/src/engine/RuleExecutor.ts +256 -0
- package/src/engine/Runner.ts +312 -0
- package/src/engine/SandboxedExecutor.ts +208 -0
- package/src/errors.ts +88 -0
- package/src/helpers/arista/helpers.ts +1220 -0
- package/src/helpers/arista/index.ts +12 -0
- package/src/helpers/aruba/helpers.ts +637 -0
- package/src/helpers/aruba/index.ts +13 -0
- package/src/helpers/cisco/helpers.ts +534 -0
- package/src/helpers/cisco/index.ts +11 -0
- package/src/helpers/common/helpers.ts +265 -0
- package/src/helpers/common/index.ts +5 -0
- package/src/helpers/common/validation.ts +280 -0
- package/src/helpers/cumulus/helpers.ts +676 -0
- package/src/helpers/cumulus/index.ts +12 -0
- package/src/helpers/extreme/helpers.ts +422 -0
- package/src/helpers/extreme/index.ts +12 -0
- package/src/helpers/fortinet/helpers.ts +892 -0
- package/src/helpers/fortinet/index.ts +12 -0
- package/src/helpers/huawei/helpers.ts +790 -0
- package/src/helpers/huawei/index.ts +11 -0
- package/src/helpers/index.ts +53 -0
- package/src/helpers/juniper/helpers.ts +756 -0
- package/src/helpers/juniper/index.ts +12 -0
- package/src/helpers/mikrotik/helpers.ts +722 -0
- package/src/helpers/mikrotik/index.ts +12 -0
- package/src/helpers/nokia/helpers.ts +856 -0
- package/src/helpers/nokia/index.ts +11 -0
- package/src/helpers/paloalto/helpers.ts +939 -0
- package/src/helpers/paloalto/index.ts +12 -0
- package/src/helpers/vyos/helpers.ts +429 -0
- package/src/helpers/vyos/index.ts +12 -0
- package/src/index.ts +30 -0
- package/src/json-rules/ExpressionEvaluator.ts +292 -0
- package/src/json-rules/HelperRegistry.ts +177 -0
- package/src/json-rules/JsonRuleCompiler.ts +339 -0
- package/src/json-rules/JsonRuleValidator.ts +371 -0
- package/src/json-rules/index.ts +97 -0
- package/src/json-rules/schema.json +350 -0
- package/src/json-rules/types.ts +303 -0
- package/src/pack-loader/PackLoader.ts +332 -0
- package/src/pack-loader/index.ts +17 -0
- package/src/pack-loader/types.ts +135 -0
- package/src/parser/IncrementalParser.ts +527 -0
- package/src/parser/Sanitizer.ts +104 -0
- package/src/parser/SchemaAwareParser.ts +504 -0
- package/src/parser/VendorSchema.ts +72 -0
- package/src/parser/vendors/arista-eos.ts +206 -0
- package/src/parser/vendors/aruba-aoscx.ts +123 -0
- package/src/parser/vendors/aruba-aosswitch.ts +113 -0
- package/src/parser/vendors/aruba-wlc.ts +173 -0
- package/src/parser/vendors/cisco-ios.ts +110 -0
- package/src/parser/vendors/cisco-nxos.ts +107 -0
- package/src/parser/vendors/cumulus-linux.ts +161 -0
- package/src/parser/vendors/extreme-exos.ts +154 -0
- package/src/parser/vendors/extreme-voss.ts +167 -0
- package/src/parser/vendors/fortinet-fortigate.ts +217 -0
- package/src/parser/vendors/huawei-vrp.ts +192 -0
- package/src/parser/vendors/index.ts +1521 -0
- package/src/parser/vendors/juniper-junos.ts +230 -0
- package/src/parser/vendors/mikrotik-routeros.ts +274 -0
- package/src/parser/vendors/nokia-sros.ts +251 -0
- package/src/parser/vendors/paloalto-panos.ts +264 -0
- package/src/parser/vendors/vyos-vyos.ts +454 -0
- package/src/types/ConfigNode.ts +72 -0
- package/src/types/DeclarativeRule.ts +158 -0
- package/src/types/IRule.ts +270 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// packages/rule-helpers/src/paloalto/index.ts
|
|
2
|
+
// Re-export all Palo Alto PAN-OS helpers
|
|
3
|
+
|
|
4
|
+
export * from './helpers';
|
|
5
|
+
|
|
6
|
+
// Also re-export commonly used common helpers for convenience
|
|
7
|
+
export {
|
|
8
|
+
hasChildCommand,
|
|
9
|
+
getChildCommand,
|
|
10
|
+
getChildCommands,
|
|
11
|
+
parseIp,
|
|
12
|
+
} from '../common/helpers';
|
|
@@ -0,0 +1,429 @@
|
|
|
1
|
+
// packages/rule-helpers/src/vyos/helpers.ts
|
|
2
|
+
// VyOS/EdgeOS-specific helper functions
|
|
3
|
+
|
|
4
|
+
import type { ConfigNode } from '../../types/ConfigNode';
|
|
5
|
+
import { hasChildCommand, getChildCommand, parseIp, prefixToMask } from '../common/helpers';
|
|
6
|
+
|
|
7
|
+
// Re-export common helpers for convenience
|
|
8
|
+
export { hasChildCommand, getChildCommand, getChildCommands, parseIp } from '../common/helpers';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Check if a VyOS interface is disabled (has "disable" statement)
|
|
12
|
+
*/
|
|
13
|
+
export const isDisabled = (node: ConfigNode): boolean => {
|
|
14
|
+
return node.children.some((child) => child.id.toLowerCase().trim() === 'disable');
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Check if interface is a physical ethernet port (ethX)
|
|
19
|
+
*/
|
|
20
|
+
export const isPhysicalVyosPort = (interfaceName: string): boolean => {
|
|
21
|
+
const name = interfaceName.toLowerCase();
|
|
22
|
+
// VyOS physical ethernet interfaces: ethernet ethX
|
|
23
|
+
// Match patterns like "ethernet eth0", "ethernet eth1", etc.
|
|
24
|
+
if (name.startsWith('ethernet eth')) {
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
// Also match just "ethX" if encountered directly
|
|
28
|
+
return /^eth\d+$/.test(name);
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Check if interface is a loopback
|
|
33
|
+
*/
|
|
34
|
+
export const isLoopback = (interfaceName: string): boolean => {
|
|
35
|
+
const name = interfaceName.toLowerCase();
|
|
36
|
+
return name.includes('loopback') || name === 'lo';
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Check if interface is a bonding interface
|
|
41
|
+
*/
|
|
42
|
+
export const isBondingInterface = (interfaceName: string): boolean => {
|
|
43
|
+
const name = interfaceName.toLowerCase();
|
|
44
|
+
return name.includes('bonding') || /^bond\d+$/.test(name);
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Check if interface is a bridge interface
|
|
49
|
+
*/
|
|
50
|
+
export const isBridgeInterface = (interfaceName: string): boolean => {
|
|
51
|
+
const name = interfaceName.toLowerCase();
|
|
52
|
+
return name.includes('bridge') || /^br\d+$/.test(name);
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Check if interface is a WireGuard interface
|
|
57
|
+
*/
|
|
58
|
+
export const isWireGuardInterface = (interfaceName: string): boolean => {
|
|
59
|
+
const name = interfaceName.toLowerCase();
|
|
60
|
+
return name.includes('wireguard') || /^wg\d+$/.test(name);
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Check if interface is a tunnel interface
|
|
65
|
+
*/
|
|
66
|
+
export const isTunnelInterface = (interfaceName: string): boolean => {
|
|
67
|
+
const name = interfaceName.toLowerCase();
|
|
68
|
+
return (
|
|
69
|
+
name.includes('tunnel') ||
|
|
70
|
+
name.includes('vti') ||
|
|
71
|
+
name.includes('vxlan') ||
|
|
72
|
+
/^(tun|vti|vxlan)\d+$/.test(name)
|
|
73
|
+
);
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Parse VyOS address format (e.g., "10.0.0.1/24")
|
|
78
|
+
* @param address The address string with CIDR notation
|
|
79
|
+
* @returns Object with ip number, prefix length, and mask, or null if invalid
|
|
80
|
+
*/
|
|
81
|
+
export const parseVyosAddress = (
|
|
82
|
+
address: string
|
|
83
|
+
): { ip: number; prefix: number; mask: number } | null => {
|
|
84
|
+
// Remove quotes if present
|
|
85
|
+
const cleanAddress = address.replace(/['"]/g, '');
|
|
86
|
+
const parts = cleanAddress.split('/');
|
|
87
|
+
if (parts.length !== 2) return null;
|
|
88
|
+
|
|
89
|
+
const ipPart = parts[0];
|
|
90
|
+
const prefixPart = parts[1];
|
|
91
|
+
if (!ipPart || !prefixPart) return null;
|
|
92
|
+
|
|
93
|
+
const ip = parseIp(ipPart);
|
|
94
|
+
const prefix = parseInt(prefixPart, 10);
|
|
95
|
+
|
|
96
|
+
if (ip === null || isNaN(prefix) || prefix < 0 || prefix > 32) {
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return {
|
|
101
|
+
ip,
|
|
102
|
+
prefix,
|
|
103
|
+
mask: prefixToMask(prefix),
|
|
104
|
+
};
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Find a stanza by name within a node's children
|
|
109
|
+
* @param node The parent ConfigNode
|
|
110
|
+
* @param stanzaName The stanza name to find
|
|
111
|
+
* @returns The matching child node, or undefined
|
|
112
|
+
*/
|
|
113
|
+
export const findStanza = (
|
|
114
|
+
node: ConfigNode,
|
|
115
|
+
stanzaName: string
|
|
116
|
+
): ConfigNode | undefined => {
|
|
117
|
+
return node.children.find(
|
|
118
|
+
(child) => child.id.toLowerCase() === stanzaName.toLowerCase()
|
|
119
|
+
);
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Find stanza by prefix (starts with)
|
|
124
|
+
* @param node The parent ConfigNode
|
|
125
|
+
* @param prefix The prefix to match
|
|
126
|
+
* @returns The matching child node, or undefined
|
|
127
|
+
*/
|
|
128
|
+
export const findStanzaByPrefix = (
|
|
129
|
+
node: ConfigNode,
|
|
130
|
+
prefix: string
|
|
131
|
+
): ConfigNode | undefined => {
|
|
132
|
+
return node.children.find((child) =>
|
|
133
|
+
child.id.toLowerCase().startsWith(prefix.toLowerCase())
|
|
134
|
+
);
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Find all stanzas matching a pattern within a node's children
|
|
139
|
+
* @param node The parent ConfigNode
|
|
140
|
+
* @param pattern The regex pattern to match
|
|
141
|
+
* @returns Array of matching child nodes
|
|
142
|
+
*/
|
|
143
|
+
export const findStanzas = (node: ConfigNode, pattern: RegExp): ConfigNode[] => {
|
|
144
|
+
return node.children.filter((child) => pattern.test(child.id.toLowerCase()));
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Find all stanzas starting with a prefix
|
|
149
|
+
* @param node The parent ConfigNode
|
|
150
|
+
* @param prefix The prefix to match
|
|
151
|
+
* @returns Array of matching child nodes
|
|
152
|
+
*/
|
|
153
|
+
export const findStanzasByPrefix = (node: ConfigNode, prefix: string): ConfigNode[] => {
|
|
154
|
+
return node.children.filter((child) =>
|
|
155
|
+
child.id.toLowerCase().startsWith(prefix.toLowerCase())
|
|
156
|
+
);
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Get all ethernet interfaces from the interfaces node
|
|
161
|
+
* @param interfacesNode The interfaces ConfigNode
|
|
162
|
+
* @returns Array of ethernet interface nodes
|
|
163
|
+
*/
|
|
164
|
+
export const getEthernetInterfaces = (interfacesNode: ConfigNode): ConfigNode[] => {
|
|
165
|
+
return interfacesNode.children.filter((child) =>
|
|
166
|
+
child.id.toLowerCase().startsWith('ethernet eth')
|
|
167
|
+
);
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Get VIF (VLAN) subinterfaces from an interface node
|
|
172
|
+
* @param interfaceNode The interface ConfigNode
|
|
173
|
+
* @returns Array of vif nodes
|
|
174
|
+
*/
|
|
175
|
+
export const getVifInterfaces = (interfaceNode: ConfigNode): ConfigNode[] => {
|
|
176
|
+
return interfaceNode.children.filter((child) =>
|
|
177
|
+
child.id.toLowerCase().startsWith('vif')
|
|
178
|
+
);
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Check if a firewall ruleset has a default action set
|
|
183
|
+
* @param rulesetNode The firewall ruleset (name X) ConfigNode
|
|
184
|
+
* @returns The default action ('drop', 'accept', 'reject') or undefined
|
|
185
|
+
*/
|
|
186
|
+
export const getFirewallDefaultAction = (
|
|
187
|
+
rulesetNode: ConfigNode
|
|
188
|
+
): 'drop' | 'accept' | 'reject' | undefined => {
|
|
189
|
+
for (const child of rulesetNode.children) {
|
|
190
|
+
const id = child.id.toLowerCase().trim();
|
|
191
|
+
if (id.startsWith('default-action')) {
|
|
192
|
+
if (id.includes('drop')) return 'drop';
|
|
193
|
+
if (id.includes('accept')) return 'accept';
|
|
194
|
+
if (id.includes('reject')) return 'reject';
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return undefined;
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Get all firewall rules from a ruleset
|
|
202
|
+
* @param rulesetNode The firewall ruleset ConfigNode
|
|
203
|
+
* @returns Array of rule nodes
|
|
204
|
+
*/
|
|
205
|
+
export const getFirewallRules = (rulesetNode: ConfigNode): ConfigNode[] => {
|
|
206
|
+
return rulesetNode.children.filter((child) =>
|
|
207
|
+
child.id.toLowerCase().startsWith('rule')
|
|
208
|
+
);
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Get the action of a firewall rule
|
|
213
|
+
* @param ruleNode The firewall rule ConfigNode
|
|
214
|
+
* @returns The action ('drop', 'accept', 'reject') or undefined
|
|
215
|
+
*/
|
|
216
|
+
export const getFirewallRuleAction = (
|
|
217
|
+
ruleNode: ConfigNode
|
|
218
|
+
): 'drop' | 'accept' | 'reject' | undefined => {
|
|
219
|
+
for (const child of ruleNode.children) {
|
|
220
|
+
const id = child.id.toLowerCase().trim();
|
|
221
|
+
if (id.startsWith('action')) {
|
|
222
|
+
if (id.includes('drop')) return 'drop';
|
|
223
|
+
if (id.includes('accept')) return 'accept';
|
|
224
|
+
if (id.includes('reject')) return 'reject';
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
return undefined;
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Check if a NAT rule has translation configured
|
|
232
|
+
* @param ruleNode The NAT rule ConfigNode
|
|
233
|
+
* @returns true if translation is configured
|
|
234
|
+
*/
|
|
235
|
+
export const hasNatTranslation = (ruleNode: ConfigNode): boolean => {
|
|
236
|
+
return ruleNode.children.some((child) =>
|
|
237
|
+
child.id.toLowerCase().startsWith('translation')
|
|
238
|
+
);
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Check if SSH service is configured in a service node
|
|
243
|
+
* @param serviceNode The service ConfigNode
|
|
244
|
+
* @returns true if SSH is configured
|
|
245
|
+
*/
|
|
246
|
+
export const hasSshService = (serviceNode: ConfigNode): boolean => {
|
|
247
|
+
return serviceNode.children.some((child) =>
|
|
248
|
+
child.id.toLowerCase().startsWith('ssh')
|
|
249
|
+
);
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Get SSH configuration from service node
|
|
254
|
+
* @param serviceNode The service ConfigNode
|
|
255
|
+
* @returns The SSH configuration node, or undefined
|
|
256
|
+
*/
|
|
257
|
+
export const getSshConfig = (serviceNode: ConfigNode): ConfigNode | undefined => {
|
|
258
|
+
return serviceNode.children.find((child) =>
|
|
259
|
+
child.id.toLowerCase().startsWith('ssh')
|
|
260
|
+
);
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Check if DHCP server is configured
|
|
265
|
+
* @param serviceNode The service ConfigNode
|
|
266
|
+
* @returns true if DHCP server is configured
|
|
267
|
+
*/
|
|
268
|
+
export const hasDhcpServer = (serviceNode: ConfigNode): boolean => {
|
|
269
|
+
return serviceNode.children.some((child) =>
|
|
270
|
+
child.id.toLowerCase().startsWith('dhcp-server')
|
|
271
|
+
);
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Get DNS forwarding configuration
|
|
276
|
+
* @param serviceNode The service ConfigNode
|
|
277
|
+
* @returns The DNS configuration node, or undefined
|
|
278
|
+
*/
|
|
279
|
+
export const getDnsConfig = (serviceNode: ConfigNode): ConfigNode | undefined => {
|
|
280
|
+
return serviceNode.children.find((child) =>
|
|
281
|
+
child.id.toLowerCase().startsWith('dns')
|
|
282
|
+
);
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Check if a system node has NTP configured
|
|
287
|
+
* @param systemNode The system ConfigNode
|
|
288
|
+
* @returns true if NTP is configured
|
|
289
|
+
*/
|
|
290
|
+
export const hasNtpConfig = (systemNode: ConfigNode): boolean => {
|
|
291
|
+
return systemNode.children.some((child) =>
|
|
292
|
+
child.id.toLowerCase().startsWith('ntp')
|
|
293
|
+
);
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Check if a system node has syslog configured
|
|
298
|
+
* @param systemNode The system ConfigNode
|
|
299
|
+
* @returns true if syslog is configured
|
|
300
|
+
*/
|
|
301
|
+
export const hasSyslogConfig = (systemNode: ConfigNode): boolean => {
|
|
302
|
+
return systemNode.children.some((child) =>
|
|
303
|
+
child.id.toLowerCase().startsWith('syslog')
|
|
304
|
+
);
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Get the login configuration from system node
|
|
309
|
+
* @param systemNode The system ConfigNode
|
|
310
|
+
* @returns The login configuration node, or undefined
|
|
311
|
+
*/
|
|
312
|
+
export const getLoginConfig = (systemNode: ConfigNode): ConfigNode | undefined => {
|
|
313
|
+
return systemNode.children.find((child) =>
|
|
314
|
+
child.id.toLowerCase().startsWith('login')
|
|
315
|
+
);
|
|
316
|
+
};
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Get all user configurations from login node
|
|
320
|
+
* @param loginNode The login ConfigNode
|
|
321
|
+
* @returns Array of user nodes
|
|
322
|
+
*/
|
|
323
|
+
export const getUserConfigs = (loginNode: ConfigNode): ConfigNode[] => {
|
|
324
|
+
return loginNode.children.filter((child) =>
|
|
325
|
+
child.id.toLowerCase().startsWith('user')
|
|
326
|
+
);
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Get all interfaces that are members of a switch (switch-port)
|
|
331
|
+
* These interfaces don't need individual IP addresses as the switch has the address
|
|
332
|
+
* @param interfacesNode The interfaces ConfigNode
|
|
333
|
+
* @returns Set of interface names (e.g., 'eth1', 'eth2') that are switch members
|
|
334
|
+
*/
|
|
335
|
+
export const getSwitchPortMembers = (interfacesNode: ConfigNode): Set<string> => {
|
|
336
|
+
const members = new Set<string>();
|
|
337
|
+
|
|
338
|
+
// Find all switch interfaces (switch switchX)
|
|
339
|
+
const switches = interfacesNode.children.filter((child) =>
|
|
340
|
+
child.id.toLowerCase().startsWith('switch ')
|
|
341
|
+
);
|
|
342
|
+
|
|
343
|
+
for (const switchNode of switches) {
|
|
344
|
+
// Find switch-port section
|
|
345
|
+
const switchPort = switchNode.children.find((child) =>
|
|
346
|
+
child.id.toLowerCase() === 'switch-port'
|
|
347
|
+
);
|
|
348
|
+
|
|
349
|
+
if (switchPort) {
|
|
350
|
+
// Find all interface members (interface ethX)
|
|
351
|
+
for (const child of switchPort.children) {
|
|
352
|
+
const match = child.id.toLowerCase().match(/^interface\s+(eth\d+)$/);
|
|
353
|
+
if (match?.[1]) {
|
|
354
|
+
members.add(match[1]);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
return members;
|
|
361
|
+
};
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* Get all interfaces that are members of a bridge
|
|
365
|
+
* These interfaces don't need individual IP addresses as the bridge has the address
|
|
366
|
+
* @param interfacesNode The interfaces ConfigNode
|
|
367
|
+
* @returns Set of interface names that are bridge members
|
|
368
|
+
*/
|
|
369
|
+
export const getBridgeMembers = (interfacesNode: ConfigNode): Set<string> => {
|
|
370
|
+
const members = new Set<string>();
|
|
371
|
+
|
|
372
|
+
// Find all bridge interfaces (bridge brX)
|
|
373
|
+
const bridges = interfacesNode.children.filter((child) =>
|
|
374
|
+
child.id.toLowerCase().startsWith('bridge ')
|
|
375
|
+
);
|
|
376
|
+
|
|
377
|
+
for (const bridgeNode of bridges) {
|
|
378
|
+
// Find member section
|
|
379
|
+
const memberSection = bridgeNode.children.find((child) =>
|
|
380
|
+
child.id.toLowerCase() === 'member'
|
|
381
|
+
);
|
|
382
|
+
|
|
383
|
+
if (memberSection) {
|
|
384
|
+
// Find all interface members within the member section
|
|
385
|
+
for (const child of memberSection.children) {
|
|
386
|
+
const match = child.id.toLowerCase().match(/^interface\s+(\S+)$/);
|
|
387
|
+
if (match?.[1]) {
|
|
388
|
+
members.add(match[1]);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
return members;
|
|
395
|
+
};
|
|
396
|
+
|
|
397
|
+
/**
|
|
398
|
+
* Get all interfaces that are members of a bonding group
|
|
399
|
+
* These interfaces don't need individual IP addresses as the bond has the address
|
|
400
|
+
* @param interfacesNode The interfaces ConfigNode
|
|
401
|
+
* @returns Set of interface names that are bonding members
|
|
402
|
+
*/
|
|
403
|
+
export const getBondingMembers = (interfacesNode: ConfigNode): Set<string> => {
|
|
404
|
+
const members = new Set<string>();
|
|
405
|
+
|
|
406
|
+
// Find all bonding interfaces (bonding bondX)
|
|
407
|
+
const bonds = interfacesNode.children.filter((child) =>
|
|
408
|
+
child.id.toLowerCase().startsWith('bonding ')
|
|
409
|
+
);
|
|
410
|
+
|
|
411
|
+
for (const bondNode of bonds) {
|
|
412
|
+
// Find member section
|
|
413
|
+
const memberSection = bondNode.children.find((child) =>
|
|
414
|
+
child.id.toLowerCase() === 'member'
|
|
415
|
+
);
|
|
416
|
+
|
|
417
|
+
if (memberSection) {
|
|
418
|
+
// Find all interface members within the member section
|
|
419
|
+
for (const child of memberSection.children) {
|
|
420
|
+
const match = child.id.toLowerCase().match(/^interface\s+(\S+)$/);
|
|
421
|
+
if (match?.[1]) {
|
|
422
|
+
members.add(match[1]);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
return members;
|
|
429
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// packages/rule-helpers/src/vyos/index.ts
|
|
2
|
+
// Re-export all VyOS/EdgeOS helpers
|
|
3
|
+
|
|
4
|
+
export * from './helpers';
|
|
5
|
+
|
|
6
|
+
// Also re-export commonly used common helpers for convenience
|
|
7
|
+
export {
|
|
8
|
+
hasChildCommand,
|
|
9
|
+
getChildCommand,
|
|
10
|
+
getChildCommands,
|
|
11
|
+
parseIp,
|
|
12
|
+
} from '../common/helpers';
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// packages/core/src/index.ts
|
|
2
|
+
|
|
3
|
+
export * from './types/ConfigNode';
|
|
4
|
+
export * from './types/IRule';
|
|
5
|
+
export * from './parser/SchemaAwareParser';
|
|
6
|
+
export * from './parser/IncrementalParser';
|
|
7
|
+
export * from './parser/VendorSchema';
|
|
8
|
+
export * from './parser/vendors';
|
|
9
|
+
export * from './engine/Runner';
|
|
10
|
+
export * from './engine/RuleExecutor';
|
|
11
|
+
export * from './parser/Sanitizer';
|
|
12
|
+
export * from './constants';
|
|
13
|
+
export * from './errors';
|
|
14
|
+
|
|
15
|
+
// SEC-012: Encrypted rule pack loader
|
|
16
|
+
export * from './pack-loader';
|
|
17
|
+
|
|
18
|
+
// SEC-001: Declarative rules and sandboxed execution
|
|
19
|
+
export * from './types/DeclarativeRule';
|
|
20
|
+
export * from './engine/SandboxedExecutor';
|
|
21
|
+
|
|
22
|
+
// JSON Rules - third-party rule authoring without TypeScript
|
|
23
|
+
export * from './json-rules';
|
|
24
|
+
|
|
25
|
+
// Rule Helpers - vendor-specific and common helper functions
|
|
26
|
+
export * as helpers from './helpers';
|
|
27
|
+
export { VENDOR_NAMESPACES, type VendorNamespace, getAllVendorModules, getVendorModule } from './helpers';
|
|
28
|
+
|
|
29
|
+
// Re-export common helpers at top level for convenience
|
|
30
|
+
export * from './helpers/common';
|