@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,1521 @@
1
+ // packages/core/src/parser/vendors/index.ts
2
+
3
+ import type { VendorSchema } from '../VendorSchema';
4
+ import { CiscoIOSSchema } from './cisco-ios';
5
+ import { CiscoNXOSSchema } from './cisco-nxos';
6
+ import { JuniperJunOSSchema } from './juniper-junos';
7
+ import { ArubaAOSCXSchema } from './aruba-aoscx';
8
+ import { ArubaAOSSwitchSchema } from './aruba-aosswitch';
9
+ import { ArubaWLCSchema } from './aruba-wlc';
10
+ import { PaloAltoPANOSSchema } from './paloalto-panos';
11
+ import { AristaEOSSchema } from './arista-eos';
12
+ import { VyOSSchema } from './vyos-vyos';
13
+ import { FortinetFortiGateSchema } from './fortinet-fortigate';
14
+ import { ExtremeEXOSSchema } from './extreme-exos';
15
+ import { ExtremeVOSSSchema } from './extreme-voss';
16
+ import { HuaweiVRPSchema } from './huawei-vrp';
17
+ import { MikroTikRouterOSSchema } from './mikrotik-routeros';
18
+ import { NokiaSROSSchema } from './nokia-sros';
19
+ import { CumulusLinuxSchema } from './cumulus-linux';
20
+
21
+ // ============================================================================
22
+ // SEC-002: ReDoS-safe helper functions
23
+ // These replace potentially dangerous multiline regex patterns with
24
+ // line-by-line processing to prevent catastrophic backtracking.
25
+ // ============================================================================
26
+
27
+ /**
28
+ * SEC-002: Safe detection for FortiOS edit/next pattern.
29
+ * Replaces: /^config\s+\S+[\s\S]*?^\s+edit\s+/m with /^\s+next$/m
30
+ * which could cause ReDoS with crafted input.
31
+ */
32
+ function hasFortiOSEditPattern(lines: string[]): boolean {
33
+ let inConfig = false;
34
+ let hasEdit = false;
35
+ for (const line of lines) {
36
+ if (/^config\s+\S+/.test(line)) {
37
+ inConfig = true;
38
+ }
39
+ if (inConfig && /^\s+edit\s+/.test(line)) {
40
+ hasEdit = true;
41
+ }
42
+ if (/^\s+next$/.test(line) && hasEdit) {
43
+ return true;
44
+ }
45
+ if (/^end$/.test(line)) {
46
+ inConfig = false;
47
+ hasEdit = false;
48
+ }
49
+ }
50
+ return false;
51
+ }
52
+
53
+ /**
54
+ * SEC-002: Safe detection for FortiOS config/set/end pattern.
55
+ * Replaces: /^config\s+\S+[\s\S]*?^\s+set\s+\S+/m with /^end$/m
56
+ */
57
+ function hasFortiOSSetPattern(lines: string[]): boolean {
58
+ let inConfig = false;
59
+ for (const line of lines) {
60
+ if (/^config\s+\S+/.test(line)) {
61
+ inConfig = true;
62
+ }
63
+ if (inConfig && /^\s+set\s+\S+/.test(line)) {
64
+ return true;
65
+ }
66
+ if (/^end$/.test(line)) {
67
+ inConfig = false;
68
+ }
69
+ }
70
+ return false;
71
+ }
72
+
73
+ /**
74
+ * SEC-002: Safe detection for Palo Alto network block with specific content.
75
+ * Replaces: /^\s*network\s*\{[\s\S]*?(ethernet\d+\/\d+|zone|virtual-router)/m
76
+ */
77
+ function hasPaloAltoNetworkBlock(lines: string[]): boolean {
78
+ let inNetworkBlock = false;
79
+ let braceDepth = 0;
80
+ for (const line of lines) {
81
+ if (/^\s*network\s*\{/.test(line)) {
82
+ inNetworkBlock = true;
83
+ braceDepth = 1;
84
+ continue;
85
+ }
86
+ if (inNetworkBlock) {
87
+ braceDepth += (line.match(/\{/g) || []).length;
88
+ braceDepth -= (line.match(/\}/g) || []).length;
89
+ if (/(ethernet\d+\/\d+|zone|virtual-router)/.test(line)) {
90
+ return true;
91
+ }
92
+ if (braceDepth <= 0) {
93
+ inNetworkBlock = false;
94
+ }
95
+ }
96
+ }
97
+ return false;
98
+ }
99
+
100
+ /**
101
+ * SEC-002: Safe detection for Palo Alto security/nat rules block.
102
+ * Replaces: /^\s*(security|nat)\s*\{[\s\S]*?rules\s*\{/m
103
+ */
104
+ function hasPaloAltoRulesBlock(lines: string[]): boolean {
105
+ let inSecurityOrNat = false;
106
+ let braceDepth = 0;
107
+ for (const line of lines) {
108
+ if (/^\s*(security|nat)\s*\{/.test(line)) {
109
+ inSecurityOrNat = true;
110
+ braceDepth = 1;
111
+ continue;
112
+ }
113
+ if (inSecurityOrNat) {
114
+ braceDepth += (line.match(/\{/g) || []).length;
115
+ braceDepth -= (line.match(/\}/g) || []).length;
116
+ if (/rules\s*\{/.test(line)) {
117
+ return true;
118
+ }
119
+ if (braceDepth <= 0) {
120
+ inSecurityOrNat = false;
121
+ }
122
+ }
123
+ }
124
+ return false;
125
+ }
126
+
127
+ /**
128
+ * SEC-002: Safe detection for Palo Alto threat profiles block.
129
+ * Replaces: /^\s*profiles\s*\{[\s\S]*?(virus|spyware|vulnerability|url-filtering|wildfire-analysis)/m
130
+ */
131
+ function hasPaloAltoProfilesBlock(lines: string[]): boolean {
132
+ let inProfiles = false;
133
+ let braceDepth = 0;
134
+ for (const line of lines) {
135
+ if (/^\s*profiles\s*\{/.test(line)) {
136
+ inProfiles = true;
137
+ braceDepth = 1;
138
+ continue;
139
+ }
140
+ if (inProfiles) {
141
+ braceDepth += (line.match(/\{/g) || []).length;
142
+ braceDepth -= (line.match(/\}/g) || []).length;
143
+ if (/(virus|spyware|vulnerability|url-filtering|wildfire-analysis)/.test(line)) {
144
+ return true;
145
+ }
146
+ if (braceDepth <= 0) {
147
+ inProfiles = false;
148
+ }
149
+ }
150
+ }
151
+ return false;
152
+ }
153
+
154
+ /**
155
+ * SEC-002: Safe detection for VyOS service block with specific services.
156
+ * Replaces: /^\s*service\s*\{[\s\S]*?(ssh|dhcp-server|dns|https|lldp)/m
157
+ */
158
+ function hasVyOSServiceBlock(lines: string[]): boolean {
159
+ let inService = false;
160
+ let braceDepth = 0;
161
+ for (const line of lines) {
162
+ if (/^\s*service\s*\{/.test(line)) {
163
+ inService = true;
164
+ braceDepth = 1;
165
+ continue;
166
+ }
167
+ if (inService) {
168
+ braceDepth += (line.match(/\{/g) || []).length;
169
+ braceDepth -= (line.match(/\}/g) || []).length;
170
+ if (/(ssh|dhcp-server|dns|https|lldp)/.test(line)) {
171
+ return true;
172
+ }
173
+ if (braceDepth <= 0) {
174
+ inService = false;
175
+ }
176
+ }
177
+ }
178
+ return false;
179
+ }
180
+
181
+ /**
182
+ * SEC-002: Safe detection for VyOS NAT with source/destination rules.
183
+ * Replaces: /^\s*nat\s*\{[\s\S]*?(source|destination)\s*\{[\s\S]*?rule\s+\d+/m
184
+ */
185
+ function hasVyOSNatRuleBlock(lines: string[]): boolean {
186
+ let inNat = false;
187
+ let inSourceOrDest = false;
188
+ let braceDepth = 0;
189
+ for (const line of lines) {
190
+ if (/^\s*nat\s*\{/.test(line)) {
191
+ inNat = true;
192
+ braceDepth = 1;
193
+ continue;
194
+ }
195
+ if (inNat) {
196
+ braceDepth += (line.match(/\{/g) || []).length;
197
+ braceDepth -= (line.match(/\}/g) || []).length;
198
+ if (/(source|destination)\s*\{/.test(line)) {
199
+ inSourceOrDest = true;
200
+ }
201
+ if (inSourceOrDest && /rule\s+\d+/.test(line)) {
202
+ return true;
203
+ }
204
+ if (braceDepth <= 0) {
205
+ inNat = false;
206
+ inSourceOrDest = false;
207
+ }
208
+ }
209
+ }
210
+ return false;
211
+ }
212
+
213
+ /**
214
+ * SEC-002: Safe detection for VyOS ethernet interface block.
215
+ * Replaces: /^\s*interfaces\s*\{[\s\S]*?ethernet\s+eth\d+\s*\{/m
216
+ */
217
+ function hasVyOSEthernetBlock(lines: string[]): boolean {
218
+ let inInterfaces = false;
219
+ let braceDepth = 0;
220
+ for (const line of lines) {
221
+ if (/^\s*interfaces\s*\{/.test(line)) {
222
+ inInterfaces = true;
223
+ braceDepth = 1;
224
+ continue;
225
+ }
226
+ if (inInterfaces) {
227
+ braceDepth += (line.match(/\{/g) || []).length;
228
+ braceDepth -= (line.match(/\}/g) || []).length;
229
+ if (/ethernet\s+eth\d+\s*\{/.test(line)) {
230
+ return true;
231
+ }
232
+ if (braceDepth <= 0) {
233
+ inInterfaces = false;
234
+ }
235
+ }
236
+ }
237
+ return false;
238
+ }
239
+
240
+ /**
241
+ * SEC-002: Safe detection for VyOS firewall name ruleset.
242
+ * Replaces: /^\s*firewall\s*\{[\s\S]*?name\s+\S+\s*\{[\s\S]*?rule\s+\d+/m
243
+ */
244
+ function hasVyOSFirewallRuleBlock(lines: string[]): boolean {
245
+ let inFirewall = false;
246
+ let inName = false;
247
+ let braceDepth = 0;
248
+ for (const line of lines) {
249
+ if (/^\s*firewall\s*\{/.test(line)) {
250
+ inFirewall = true;
251
+ braceDepth = 1;
252
+ continue;
253
+ }
254
+ if (inFirewall) {
255
+ braceDepth += (line.match(/\{/g) || []).length;
256
+ braceDepth -= (line.match(/\}/g) || []).length;
257
+ if (/name\s+\S+\s*\{/.test(line)) {
258
+ inName = true;
259
+ }
260
+ if (inName && /rule\s+\d+/.test(line)) {
261
+ return true;
262
+ }
263
+ if (braceDepth <= 0) {
264
+ inFirewall = false;
265
+ inName = false;
266
+ }
267
+ }
268
+ }
269
+ return false;
270
+ }
271
+
272
+ /**
273
+ * SEC-002: Safe detection for VyOS high-availability/VRRP block.
274
+ * Replaces: /^\s*high-availability\s*\{[\s\S]*?vrrp\s*\{/m
275
+ */
276
+ function hasVyOSHighAvailabilityBlock(lines: string[]): boolean {
277
+ let inHA = false;
278
+ let braceDepth = 0;
279
+ for (const line of lines) {
280
+ if (/^\s*high-availability\s*\{/.test(line)) {
281
+ inHA = true;
282
+ braceDepth = 1;
283
+ continue;
284
+ }
285
+ if (inHA) {
286
+ braceDepth += (line.match(/\{/g) || []).length;
287
+ braceDepth -= (line.match(/\}/g) || []).length;
288
+ if (/vrrp\s*\{/.test(line)) {
289
+ return true;
290
+ }
291
+ if (braceDepth <= 0) {
292
+ inHA = false;
293
+ }
294
+ }
295
+ }
296
+ return false;
297
+ }
298
+
299
+ /**
300
+ * SEC-002: Safe detection for VyOS VPN IPsec site-to-site.
301
+ * Replaces: /^\s*vpn\s*\{[\s\S]*?ipsec\s*\{[\s\S]*?site-to-site/m
302
+ */
303
+ function hasVyOSVpnIpsecBlock(lines: string[]): boolean {
304
+ let inVpn = false;
305
+ let inIpsec = false;
306
+ let braceDepth = 0;
307
+ for (const line of lines) {
308
+ if (/^\s*vpn\s*\{/.test(line)) {
309
+ inVpn = true;
310
+ braceDepth = 1;
311
+ continue;
312
+ }
313
+ if (inVpn) {
314
+ braceDepth += (line.match(/\{/g) || []).length;
315
+ braceDepth -= (line.match(/\}/g) || []).length;
316
+ if (/ipsec\s*\{/.test(line)) {
317
+ inIpsec = true;
318
+ }
319
+ if (inIpsec && /site-to-site/.test(line)) {
320
+ return true;
321
+ }
322
+ if (braceDepth <= 0) {
323
+ inVpn = false;
324
+ inIpsec = false;
325
+ }
326
+ }
327
+ }
328
+ return false;
329
+ }
330
+
331
+ /**
332
+ * SEC-002: Safe detection for VyOS protocols static routes.
333
+ * Replaces: /^\s*protocols\s*\{[\s\S]*?static\s*\{[\s\S]*?route\s+[\d.\/]+\s*\{/m
334
+ */
335
+ function hasVyOSStaticRouteBlock(lines: string[]): boolean {
336
+ let inProtocols = false;
337
+ let inStatic = false;
338
+ let braceDepth = 0;
339
+ for (const line of lines) {
340
+ if (/^\s*protocols\s*\{/.test(line)) {
341
+ inProtocols = true;
342
+ braceDepth = 1;
343
+ continue;
344
+ }
345
+ if (inProtocols) {
346
+ braceDepth += (line.match(/\{/g) || []).length;
347
+ braceDepth -= (line.match(/\}/g) || []).length;
348
+ if (/static\s*\{/.test(line)) {
349
+ inStatic = true;
350
+ }
351
+ if (inStatic && /route\s+[\d.\/]+\s*\{/.test(line)) {
352
+ return true;
353
+ }
354
+ if (braceDepth <= 0) {
355
+ inProtocols = false;
356
+ inStatic = false;
357
+ }
358
+ }
359
+ }
360
+ return false;
361
+ }
362
+
363
+ /**
364
+ * SEC-002: Safe detection for Nokia SR OS router block with interface.
365
+ * Replaces: /^\s+router\s*[\s\S]*?interface\s+"[^"]+"/m
366
+ */
367
+ function hasNokiaRouterInterfaceBlock(lines: string[]): boolean {
368
+ let inRouter = false;
369
+ for (const line of lines) {
370
+ if (/^\s+router\s*(".*")?$/.test(line)) {
371
+ inRouter = true;
372
+ continue;
373
+ }
374
+ if (inRouter) {
375
+ if (/interface\s+"[^"]+"/.test(line)) {
376
+ return true;
377
+ }
378
+ // Exit router block on unindented line (excluding blank lines)
379
+ if (line.trim() && !/^\s/.test(line)) {
380
+ inRouter = false;
381
+ }
382
+ }
383
+ }
384
+ return false;
385
+ }
386
+
387
+ /**
388
+ * SEC-002: Safe detection for Nokia SR OS system name.
389
+ * Replaces: /^\s+system[\s\S]*?name\s+"[^"]+"/m
390
+ */
391
+ function hasNokiaSystemNameBlock(lines: string[]): boolean {
392
+ let inSystem = false;
393
+ for (const line of lines) {
394
+ if (/^\s+system\s*$/.test(line)) {
395
+ inSystem = true;
396
+ continue;
397
+ }
398
+ if (inSystem) {
399
+ if (/name\s+"[^"]+"/.test(line)) {
400
+ return true;
401
+ }
402
+ if (line.trim() && !/^\s/.test(line)) {
403
+ inSystem = false;
404
+ }
405
+ }
406
+ }
407
+ return false;
408
+ }
409
+
410
+ /**
411
+ * SEC-002: Safe detection for Nokia SR OS BGP group.
412
+ * Replaces: /^\s+bgp[\s\S]*?group\s+"[^"]+"/m
413
+ */
414
+ function hasNokiaBgpGroupBlock(lines: string[]): boolean {
415
+ let inBgp = false;
416
+ for (const line of lines) {
417
+ if (/^\s+bgp\s*$/.test(line)) {
418
+ inBgp = true;
419
+ continue;
420
+ }
421
+ if (inBgp) {
422
+ if (/group\s+"[^"]+"/.test(line)) {
423
+ return true;
424
+ }
425
+ if (line.trim() && !/^\s/.test(line)) {
426
+ inBgp = false;
427
+ }
428
+ }
429
+ }
430
+ return false;
431
+ }
432
+
433
+ /**
434
+ * SEC-002: Safe detection for Nokia SR OS port with admin-state.
435
+ * Replaces: /^port\s+\d+\/\d+\/\d+[\s\S]*?admin-state/m
436
+ */
437
+ function hasNokiaPortAdminState(lines: string[]): boolean {
438
+ let inPort = false;
439
+ for (const line of lines) {
440
+ if (/^port\s+\d+\/\d+\/\d+/.test(line)) {
441
+ inPort = true;
442
+ continue;
443
+ }
444
+ if (inPort) {
445
+ if (/admin-state/.test(line)) {
446
+ return true;
447
+ }
448
+ // Exit port block on non-indented line
449
+ if (line.trim() && !/^\s/.test(line)) {
450
+ inPort = false;
451
+ }
452
+ }
453
+ }
454
+ return false;
455
+ }
456
+
457
+ /**
458
+ * SEC-002: Safe detection for Nokia SR OS card/MDA.
459
+ * Replaces: /^card\s+\d+[\s\S]*?mda\s+\d+/m
460
+ */
461
+ function hasNokiaCardMdaBlock(lines: string[]): boolean {
462
+ let inCard = false;
463
+ for (const line of lines) {
464
+ if (/^card\s+\d+/.test(line)) {
465
+ inCard = true;
466
+ continue;
467
+ }
468
+ if (inCard) {
469
+ if (/mda\s+\d+/.test(line)) {
470
+ return true;
471
+ }
472
+ if (line.trim() && !/^\s/.test(line)) {
473
+ inCard = false;
474
+ }
475
+ }
476
+ }
477
+ return false;
478
+ }
479
+
480
+ /**
481
+ * SEC-002: Safe detection for VOSS router isis with SPBM.
482
+ * Replaces: /^router\s+isis[\s\S]*?spbm\s+\d+/m
483
+ */
484
+ function hasVossRouterIsisSpbm(lines: string[]): boolean {
485
+ let inIsis = false;
486
+ for (const line of lines) {
487
+ if (/^router\s+isis/.test(line)) {
488
+ inIsis = true;
489
+ continue;
490
+ }
491
+ if (inIsis) {
492
+ if (/spbm\s+\d+/.test(line)) {
493
+ return true;
494
+ }
495
+ // Exit on another top-level command
496
+ if (line.trim() && !/^\s/.test(line) && !/^!/.test(line)) {
497
+ inIsis = false;
498
+ }
499
+ }
500
+ }
501
+ return false;
502
+ }
503
+
504
+ /**
505
+ * SEC-002: Safe detection for Huawei AAA block.
506
+ * Replaces: /^aaa\s*$/m with /^\s+(authentication-scheme|authorization-scheme|local-user)/m
507
+ */
508
+ function hasHuaweiAaaBlock(lines: string[]): boolean {
509
+ let inAaa = false;
510
+ for (const line of lines) {
511
+ if (/^aaa\s*$/.test(line)) {
512
+ inAaa = true;
513
+ continue;
514
+ }
515
+ if (inAaa) {
516
+ if (/^\s+(authentication-scheme|authorization-scheme|local-user)/.test(line)) {
517
+ return true;
518
+ }
519
+ if (line.trim() && !/^\s/.test(line) && !/^#/.test(line)) {
520
+ inAaa = false;
521
+ }
522
+ }
523
+ }
524
+ return false;
525
+ }
526
+
527
+ /** All registered vendor schemas */
528
+ export const vendorSchemas: VendorSchema[] = [
529
+ CiscoIOSSchema,
530
+ CiscoNXOSSchema,
531
+ JuniperJunOSSchema,
532
+ ArubaAOSCXSchema,
533
+ ArubaAOSSwitchSchema,
534
+ ArubaWLCSchema,
535
+ PaloAltoPANOSSchema,
536
+ AristaEOSSchema,
537
+ VyOSSchema,
538
+ FortinetFortiGateSchema,
539
+ ExtremeEXOSSchema,
540
+ ExtremeVOSSSchema,
541
+ HuaweiVRPSchema,
542
+ MikroTikRouterOSSchema,
543
+ NokiaSROSSchema,
544
+ CumulusLinuxSchema,
545
+ ];
546
+
547
+ /** Default vendor when none specified or detection fails */
548
+ export const defaultVendor = CiscoIOSSchema;
549
+
550
+ /**
551
+ * Get vendor schema by ID.
552
+ * @param vendorId The vendor identifier (e.g., 'cisco-ios', 'juniper-junos')
553
+ * @returns The matching VendorSchema
554
+ * @throws Error if vendor not found
555
+ */
556
+ export function getVendor(vendorId: string): VendorSchema {
557
+ const vendor = vendorSchemas.find((v) => v.id === vendorId);
558
+ if (!vendor) {
559
+ const available = vendorSchemas.map((v) => v.id).join(', ');
560
+ throw new Error(`Unknown vendor: ${vendorId}. Available: ${available}`);
561
+ }
562
+ return vendor;
563
+ }
564
+
565
+ /**
566
+ * Check if a vendor ID is valid.
567
+ * @param vendorId The vendor identifier to check
568
+ * @returns true if the vendor exists
569
+ */
570
+ export function isValidVendor(vendorId: string): boolean {
571
+ return vendorSchemas.some((v) => v.id === vendorId);
572
+ }
573
+
574
+ /**
575
+ * Get all available vendor IDs.
576
+ * @returns Array of vendor identifiers
577
+ */
578
+ export function getAvailableVendors(): string[] {
579
+ return vendorSchemas.map((v) => v.id);
580
+ }
581
+
582
+ /**
583
+ * Vendor info for display purposes.
584
+ */
585
+ export interface VendorInfo {
586
+ id: string;
587
+ name: string;
588
+ }
589
+
590
+ /**
591
+ * Get all available vendors with their display names.
592
+ * @returns Array of vendor info objects
593
+ */
594
+ export function getAvailableVendorInfo(): VendorInfo[] {
595
+ return vendorSchemas.map((v) => ({ id: v.id, name: v.name }));
596
+ }
597
+
598
+ /**
599
+ * Auto-detect vendor from configuration text.
600
+ * Uses heuristics based on syntax patterns unique to each vendor.
601
+ *
602
+ * Detection priority:
603
+ * 1. Juniper JunOS - brace-based hierarchy, set commands
604
+ * 2. Aruba WLC - profile-based WLAN configuration
605
+ * 3. Aruba AOS-CX - modern switch syntax
606
+ * 4. Aruba AOS-Switch - legacy ProCurve syntax
607
+ * 5. Cisco NX-OS - feature commands, VDC
608
+ * 6. Cisco IOS - default fallback
609
+ *
610
+ * SEC-002: ReDoS protection implemented via:
611
+ * - Reduced sample size (2000 chars for initial pass)
612
+ * - Line-by-line processing for complex pattern detection
613
+ * - Replaced dangerous [\s\S]*? patterns with safe helper functions
614
+ *
615
+ * @param configText The configuration text to analyze
616
+ * @returns The detected VendorSchema (defaults to Cisco IOS)
617
+ */
618
+ export function detectVendor(configText: string): VendorSchema {
619
+ // SEC-002: Analyze first portion of config for detection patterns
620
+ // Reduced from 4000 to 2000 chars for better ReDoS protection
621
+ const sampleText = configText.slice(0, 2000);
622
+
623
+ // SEC-002: Pre-split lines for safe helper functions
624
+ // This is done once and reused by multiple detection functions
625
+ const lines = sampleText.split('\n');
626
+
627
+ // ============ NVIDIA Cumulus Linux Detection ============
628
+ // Cumulus uses NCLU (net add/del), NVUE (nv set/unset), or Debian-style ifupdown2
629
+ // Must be checked early due to unique command prefixes
630
+
631
+ // NCLU commands: net add interface, net add bgp, net add vlan
632
+ if (/^net\s+(add|del)\s+(interface|bgp|ospf|vlan|bond|bridge|clag|routing|loopback)\s+/m.test(sampleText)) {
633
+ return CumulusLinuxSchema;
634
+ }
635
+
636
+ // NVUE commands: nv set interface, nv set router bgp, nv set bridge
637
+ if (/^nv\s+(set|unset)\s+(interface|router|bridge|vrf|system|service|evpn|nve|qos)\s+/m.test(sampleText)) {
638
+ return CumulusLinuxSchema;
639
+ }
640
+
641
+ // NVUE config commands: nv config apply, nv config save
642
+ if (/^nv\s+config\s+(apply|save|diff|patch|replace)/m.test(sampleText)) {
643
+ return CumulusLinuxSchema;
644
+ }
645
+
646
+ // Cumulus-specific interface naming: swpN (switch ports)
647
+ // Combined with Debian ifupdown2 syntax
648
+ if (/^auto\s+swp\d+/m.test(sampleText) || /^iface\s+swp\d+/m.test(sampleText)) {
649
+ return CumulusLinuxSchema;
650
+ }
651
+
652
+ // Cumulus MLAG (CLAG) configuration
653
+ if (/^net\s+add\s+clag\s+peer/m.test(sampleText) || /^clagd-/m.test(sampleText)) {
654
+ return CumulusLinuxSchema;
655
+ }
656
+
657
+ // Cumulus bridge configuration with bridge-vids, bridge-pvid (VLAN-aware bridge)
658
+ if (/^\s+bridge-vids\s+/m.test(sampleText) && /^\s+bridge-pvid\s+/m.test(sampleText)) {
659
+ return CumulusLinuxSchema;
660
+ }
661
+
662
+ // Cumulus bridge-vlan-aware directive (distinctive)
663
+ if (/^\s+bridge-vlan-aware\s+yes/m.test(sampleText)) {
664
+ return CumulusLinuxSchema;
665
+ }
666
+
667
+ // Cumulus peerlink interface (MLAG)
668
+ if (/^auto\s+peerlink/m.test(sampleText) || /^iface\s+peerlink/m.test(sampleText)) {
669
+ return CumulusLinuxSchema;
670
+ }
671
+
672
+ // Cumulus bond with clag-id
673
+ if (/^\s+clag-id\s+\d+/m.test(sampleText)) {
674
+ return CumulusLinuxSchema;
675
+ }
676
+
677
+ // Cumulus vlan-raw-device directive
678
+ if (/^\s+vlan-raw-device\s+/m.test(sampleText)) {
679
+ return CumulusLinuxSchema;
680
+ }
681
+
682
+ // ============ MikroTik RouterOS Detection ============
683
+ // RouterOS uses distinctive path-based syntax with forward slashes
684
+ // Must be checked early as paths like /ip could be confused with comments
685
+
686
+ // MikroTik interface paths: /interface ethernet, /interface vlan, /interface bridge
687
+ if (/^\/interface\s+(ethernet|vlan|bridge|wireless|bonding|wireguard)/m.test(sampleText)) {
688
+ return MikroTikRouterOSSchema;
689
+ }
690
+
691
+ // MikroTik IP configuration: /ip address, /ip firewall, /ip route, /ip dns
692
+ if (/^\/ip\s+(address|firewall|route|dns|pool|dhcp-server|dhcp-client|service)/m.test(sampleText)) {
693
+ return MikroTikRouterOSSchema;
694
+ }
695
+
696
+ // MikroTik system identity (hostname equivalent)
697
+ if (/^\/system\s+identity/m.test(sampleText)) {
698
+ return MikroTikRouterOSSchema;
699
+ }
700
+
701
+ // MikroTik routing protocols
702
+ if (/^\/routing\s+(bgp|ospf|filter|bfd|id)/m.test(sampleText)) {
703
+ return MikroTikRouterOSSchema;
704
+ }
705
+
706
+ // MikroTik user management
707
+ if (/^\/user\s*$/m.test(sampleText) || /^\/user\s+group/m.test(sampleText)) {
708
+ return MikroTikRouterOSSchema;
709
+ }
710
+
711
+ // MikroTik queue configuration
712
+ if (/^\/queue\s+(simple|tree|type)/m.test(sampleText)) {
713
+ return MikroTikRouterOSSchema;
714
+ }
715
+
716
+ // MikroTik tools
717
+ if (/^\/tool\s+(bandwidth-server|netwatch|mac-server|e-mail|graphing)/m.test(sampleText)) {
718
+ return MikroTikRouterOSSchema;
719
+ }
720
+
721
+ // MikroTik CAPsMAN (wireless controller)
722
+ if (/^\/caps-man\s/m.test(sampleText)) {
723
+ return MikroTikRouterOSSchema;
724
+ }
725
+
726
+ // MikroTik distinctive find expression syntax: [ find default-name=ether1 ]
727
+ if (/\[\s*find\s+[a-z-]+=["']?[^\]]+["']?\s*\]/m.test(sampleText)) {
728
+ return MikroTikRouterOSSchema;
729
+ }
730
+
731
+ // MikroTik add/set commands with property=value syntax under path blocks
732
+ // Distinctive pattern: add chain=input action=accept
733
+ if (/^add\s+[a-z-]+=\S+.*[a-z-]+=\S+/m.test(sampleText) && /^\/[a-z]/m.test(sampleText)) {
734
+ return MikroTikRouterOSSchema;
735
+ }
736
+
737
+ // MikroTik SNMP and certificates
738
+ if (/^\/snmp\s*$/m.test(sampleText) || /^\/certificate\s*$/m.test(sampleText)) {
739
+ return MikroTikRouterOSSchema;
740
+ }
741
+
742
+ // MikroTik PPP configuration
743
+ if (/^\/ppp\s+(profile|secret|aaa)/m.test(sampleText)) {
744
+ return MikroTikRouterOSSchema;
745
+ }
746
+
747
+ // MikroTik system logging, NTP, scheduler
748
+ if (/^\/system\s+(logging|ntp|scheduler|script|clock)/m.test(sampleText)) {
749
+ return MikroTikRouterOSSchema;
750
+ }
751
+
752
+ // ============ Fortinet FortiGate (FortiOS) Detection ============
753
+ // FortiOS uses distinctive config/edit/next/end syntax
754
+
755
+ // FortiOS config blocks: "config system global", "config firewall policy"
756
+ if (/^config\s+system\s+(global|interface|admin|dns|ntp|ha|settings)/m.test(sampleText)) {
757
+ return FortinetFortiGateSchema;
758
+ }
759
+
760
+ // FortiOS firewall config: "config firewall policy", "config firewall address"
761
+ if (/^config\s+firewall\s+(policy|address|addrgrp|service|vip|ippool)/m.test(sampleText)) {
762
+ return FortinetFortiGateSchema;
763
+ }
764
+
765
+ // FortiOS VPN config: "config vpn ipsec phase1-interface"
766
+ if (/^config\s+vpn\s+(ipsec|ssl)/m.test(sampleText)) {
767
+ return FortinetFortiGateSchema;
768
+ }
769
+
770
+ // FortiOS router config: "config router static", "config router bgp"
771
+ if (/^config\s+router\s+(static|bgp|ospf|policy|rip|access-list|prefix-list|route-map)/m.test(sampleText)) {
772
+ return FortinetFortiGateSchema;
773
+ }
774
+
775
+ // FortiOS security profiles: "config antivirus profile", "config webfilter profile"
776
+ if (/^config\s+(antivirus|webfilter|ips|application|dlp|spamfilter|emailfilter|dnsfilter|waf|voip)\s+/m.test(sampleText)) {
777
+ return FortinetFortiGateSchema;
778
+ }
779
+
780
+ // FortiOS user config: "config user local", "config user ldap"
781
+ if (/^config\s+user\s+(local|group|ldap|radius|tacacs|fsso)/m.test(sampleText)) {
782
+ return FortinetFortiGateSchema;
783
+ }
784
+
785
+ // FortiOS log config: "config log syslogd setting"
786
+ if (/^config\s+log\s+(syslogd|fortianalyzer|disk|memory)/m.test(sampleText)) {
787
+ return FortinetFortiGateSchema;
788
+ }
789
+
790
+ // FortiOS edit with next pattern (very distinctive)
791
+ // SEC-002: Use safe line-by-line helper instead of dangerous [\s\S]*? regex
792
+ if (hasFortiOSEditPattern(lines)) {
793
+ return FortinetFortiGateSchema;
794
+ }
795
+
796
+ // FortiOS set commands within config blocks
797
+ // SEC-002: Use safe line-by-line helper instead of dangerous [\s\S]*? regex
798
+ if (hasFortiOSSetPattern(lines) && /^end$/m.test(sampleText)) {
799
+ return FortinetFortiGateSchema;
800
+ }
801
+
802
+ // ============ Palo Alto PAN-OS Detection ============
803
+ // PAN-OS uses distinctive top-level stanzas and set commands
804
+
805
+ // Palo Alto hierarchical format: "deviceconfig {", "rulebase {"
806
+ if (/^\s*(deviceconfig|rulebase|mgt-config|vsys\d*)\s*\{/m.test(sampleText)) {
807
+ return PaloAltoPANOSSchema;
808
+ }
809
+
810
+ // Palo Alto network config with zone or interface naming
811
+ // SEC-002: Use safe line-by-line helper instead of dangerous [\s\S]*? regex
812
+ if (hasPaloAltoNetworkBlock(lines)) {
813
+ return PaloAltoPANOSSchema;
814
+ }
815
+
816
+ // Palo Alto set commands format
817
+ // Note: 'service' in Palo Alto refers to service objects (HTTP, HTTPS, etc.)
818
+ // NOT service daemons like VyOS (service ssh, service dhcp-server)
819
+ // Palo Alto service objects: "set service <name> protocol tcp port <port>"
820
+ if (/^set\s+(deviceconfig|rulebase|network\s+interface\s+ethernet|address|zone)\s+/m.test(sampleText)) {
821
+ return PaloAltoPANOSSchema;
822
+ }
823
+
824
+ // Palo Alto service objects (more specific pattern)
825
+ if (/^set\s+service\s+\S+\s+protocol\s+(tcp|udp)/m.test(sampleText)) {
826
+ return PaloAltoPANOSSchema;
827
+ }
828
+
829
+ // Palo Alto security rules format
830
+ // SEC-002: Use safe line-by-line helper instead of dangerous [\s\S]*? regex
831
+ if (hasPaloAltoRulesBlock(lines)) {
832
+ return PaloAltoPANOSSchema;
833
+ }
834
+
835
+ // Palo Alto threat profiles
836
+ // SEC-002: Use safe line-by-line helper instead of dangerous [\s\S]*? regex
837
+ if (hasPaloAltoProfilesBlock(lines)) {
838
+ return PaloAltoPANOSSchema;
839
+ }
840
+
841
+ // Palo Alto GlobalProtect
842
+ if (/^\s*global-protect\s*\{/m.test(sampleText)) {
843
+ return PaloAltoPANOSSchema;
844
+ }
845
+
846
+ // Panorama specific constructs
847
+ if (/^\s*(device-group|template|template-stack|shared)\s*\{/m.test(sampleText)) {
848
+ return PaloAltoPANOSSchema;
849
+ }
850
+
851
+ // ============ VyOS/EdgeOS Detection ============
852
+ // VyOS uses brace-based hierarchy with distinctive top-level stanzas
853
+ // Must be checked before JunOS since both use braces and 'set' commands
854
+
855
+ // VyOS set commands with distinctive paths
856
+ // VyOS uses 'set interfaces ethernet' (not 'set interfaces ge-')
857
+ if (/^set\s+(interfaces\s+ethernet|service\s+ssh|nat\s+source|firewall\s+name|high-availability|vpn\s+ipsec|traffic-policy|container\s+name)\s+/m.test(sampleText)) {
858
+ return VyOSSchema;
859
+ }
860
+
861
+ // VyOS hierarchical format with distinctive stanzas
862
+ // 'service' and 'nat' as top-level are VyOS-specific (not JunOS)
863
+ // SEC-002: Use safe line-by-line helper instead of dangerous [\s\S]*? regex
864
+ if (hasVyOSServiceBlock(lines)) {
865
+ return VyOSSchema;
866
+ }
867
+
868
+ // VyOS NAT structure (source/destination rules)
869
+ // SEC-002: Use safe line-by-line helper instead of dangerous [\s\S]*? regex
870
+ if (hasVyOSNatRuleBlock(lines)) {
871
+ return VyOSSchema;
872
+ }
873
+
874
+ // VyOS ethernet interface naming (eth0, eth1, etc.)
875
+ // SEC-002: Use safe line-by-line helper instead of dangerous [\s\S]*? regex
876
+ if (hasVyOSEthernetBlock(lines)) {
877
+ return VyOSSchema;
878
+ }
879
+
880
+ // VyOS set commands for ethernet interfaces
881
+ if (/^set\s+interfaces\s+ethernet\s+eth\d+/m.test(sampleText)) {
882
+ return VyOSSchema;
883
+ }
884
+
885
+ // VyOS firewall name ruleset (vs JunOS filter)
886
+ // SEC-002: Use safe line-by-line helper instead of dangerous [\s\S]*? regex
887
+ if (hasVyOSFirewallRuleBlock(lines)) {
888
+ return VyOSSchema;
889
+ }
890
+
891
+ // VyOS zone-based firewall
892
+ if (/^set\s+firewall\s+zone\s+/m.test(sampleText)) {
893
+ return VyOSSchema;
894
+ }
895
+
896
+ // VyOS bonding/bridge interfaces (distinctive names)
897
+ if (/^set\s+interfaces\s+(bonding\s+bond\d+|bridge\s+br\d+|wireguard\s+wg\d+)/m.test(sampleText)) {
898
+ return VyOSSchema;
899
+ }
900
+
901
+ // VyOS high-availability/VRRP structure
902
+ // SEC-002: Use safe line-by-line helper instead of dangerous [\s\S]*? regex
903
+ if (hasVyOSHighAvailabilityBlock(lines)) {
904
+ return VyOSSchema;
905
+ }
906
+
907
+ // VyOS VPN IPsec site-to-site
908
+ // SEC-002: Use safe line-by-line helper instead of dangerous [\s\S]*? regex
909
+ if (hasVyOSVpnIpsecBlock(lines)) {
910
+ return VyOSSchema;
911
+ }
912
+
913
+ // VyOS protocols static routes structure
914
+ // SEC-002: Use safe line-by-line helper instead of dangerous [\s\S]*? regex
915
+ if (hasVyOSStaticRouteBlock(lines)) {
916
+ return VyOSSchema;
917
+ }
918
+
919
+ // VyOS/EdgeOS comment format /* comment */
920
+ if (/^\/\*\s*.+\s*\*\/$/m.test(sampleText) && /^\s*(interfaces|firewall|nat|service|protocols)\s*\{/m.test(sampleText)) {
921
+ return VyOSSchema;
922
+ }
923
+
924
+ // ============ Juniper JunOS Detection ============
925
+ // JunOS uses brace-based hierarchy and has distinctive top-level stanzas
926
+
927
+ // Hierarchical format with braces: "system {", "interfaces {"
928
+ // These patterns are unique to JunOS display format
929
+ if (/^\s*(system|chassis|interfaces|protocols|policy-options|routing-options|routing-instances|security|firewall|class-of-service|vlans|bridge-domains)\s*\{/m.test(sampleText)) {
930
+ return JuniperJunOSSchema;
931
+ }
932
+
933
+ // Set commands format: "set interfaces ge-0/0/0"
934
+ // JunOS flat configuration format
935
+ if (/^set\s+(system|chassis|interfaces|protocols|policy-options|routing-options|routing-instances|security|firewall)/m.test(sampleText)) {
936
+ return JuniperJunOSSchema;
937
+ }
938
+
939
+ // JunOS-style interface names (ge-, xe-, et-, ae-, lo0, etc.)
940
+ // Combined with brace on same line or next line
941
+ if (/^\s*(ge|xe|et|ae|lo|irb|vlan|em|fxp)-[\d\/:.]+\s*\{/m.test(sampleText)) {
942
+ return JuniperJunOSSchema;
943
+ }
944
+
945
+ // JunOS version statement
946
+ if (/^version\s+[\d.]+[A-Z]\d+/m.test(sampleText)) {
947
+ return JuniperJunOSSchema;
948
+ }
949
+
950
+ // ============ Aruba WLC Detection ============
951
+ // ArubaOS WLC uses profile-based WLAN configuration
952
+
953
+ // WLAN SSID profiles and virtual-AP (most distinctive)
954
+ if (/^wlan\s+(ssid-profile|virtual-ap)\s+["']?[^"'\n]+["']?/m.test(sampleText)) {
955
+ return ArubaWLCSchema;
956
+ }
957
+
958
+ // AP groups (wireless controller specific)
959
+ if (/^ap-group\s+["']?[^"'\n]+["']?/m.test(sampleText)) {
960
+ return ArubaWLCSchema;
961
+ }
962
+
963
+ // AAA authentication-server radius with quoted name (WLC style)
964
+ if (/^aaa\s+authentication-server\s+radius\s+["'][^"']+["']/m.test(sampleText)) {
965
+ return ArubaWLCSchema;
966
+ }
967
+
968
+ // RF profiles (ARM, dot11a, dot11g)
969
+ if (/^rf\s+(arm-profile|dot11[ag]-radio-profile)\s+["']?[^"'\n]+["']?/m.test(sampleText)) {
970
+ return ArubaWLCSchema;
971
+ }
972
+
973
+ // AAA profile with quoted name (WLC style)
974
+ if (/^aaa\s+profile\s+["'][^"']+["']/m.test(sampleText)) {
975
+ return ArubaWLCSchema;
976
+ }
977
+
978
+ // ============ Aruba AOS-CX Detection ============
979
+ // AOS-CX uses Cisco-like syntax but with distinctive patterns
980
+
981
+ // AOS-CX version string
982
+ if (/^!Version\s+ArubaOS-CX/m.test(sampleText)) {
983
+ return ArubaAOSCXSchema;
984
+ }
985
+
986
+ // AOS-CX interface naming: slot/member/port format (1/1/1)
987
+ if (/^interface\s+\d+\/\d+\/\d+/m.test(sampleText)) {
988
+ return ArubaAOSCXSchema;
989
+ }
990
+
991
+ // AOS-CX specific VLAN commands under interface
992
+ if (/^\s+vlan\s+(access|trunk\s+(native|allowed))\s+\d+/m.test(sampleText)) {
993
+ return ArubaAOSCXSchema;
994
+ }
995
+
996
+ // AOS-CX LAG interface
997
+ if (/^interface\s+lag\s+\d+/m.test(sampleText)) {
998
+ return ArubaAOSCXSchema;
999
+ }
1000
+
1001
+ // AOS-CX VSX configuration
1002
+ if (/^vsx\s*$/m.test(sampleText) || /^vsx-sync\s+/m.test(sampleText)) {
1003
+ return ArubaAOSCXSchema;
1004
+ }
1005
+
1006
+ // ============ Aruba AOS-Switch Detection ============
1007
+ // AOS-Switch (ProVision) uses VLAN-centric configuration
1008
+
1009
+ // ProCurve/ProVision configuration editor header
1010
+ if (/^;\s*[A-Z]\d+\w+\s+Configuration\s+Editor/m.test(sampleText)) {
1011
+ return ArubaAOSSwitchSchema;
1012
+ }
1013
+
1014
+ // AOS-Switch VLAN with tagged/untagged port lists
1015
+ // Match either: vlan X\n tagged/untagged OR just indented tagged/untagged commands
1016
+ if (/^vlan\s+\d+[\s\S]*?^\s+(tagged|untagged)\s+[\dA-Za-z][\d,-]*/m.test(sampleText)) {
1017
+ return ArubaAOSSwitchSchema;
1018
+ }
1019
+
1020
+ // Also detect if we see indented tagged/untagged patterns (AOS-Switch specific)
1021
+ if (/^\s+(tagged|untagged)\s+[\dA-Za-z,-]+\s*$/m.test(sampleText)) {
1022
+ return ArubaAOSSwitchSchema;
1023
+ }
1024
+
1025
+ // AOS-Switch hostname with ProCurve or Aruba prefix
1026
+ if (/^hostname\s+["']?(ProCurve|Aruba)/m.test(sampleText)) {
1027
+ return ArubaAOSSwitchSchema;
1028
+ }
1029
+
1030
+ // AOS-Switch specific: trunk command for LAG (not Cisco trunk)
1031
+ // Distinguished by port numbers following trunk name
1032
+ if (/^trunk\s+[\dA-Za-z][\d,-]+\s+\w+/m.test(sampleText)) {
1033
+ return ArubaAOSSwitchSchema;
1034
+ }
1035
+
1036
+ // AOS-Switch specific: timesync sntp
1037
+ if (/^timesync\s+sntp/m.test(sampleText)) {
1038
+ return ArubaAOSSwitchSchema;
1039
+ }
1040
+
1041
+ // ============ Cisco NX-OS Detection ============
1042
+ // NX-OS has feature activation commands and VDC support
1043
+
1044
+ // Feature commands at the start of config
1045
+ if (/^feature\s+\w+/m.test(sampleText)) {
1046
+ return CiscoNXOSSchema;
1047
+ }
1048
+
1049
+ // NX-OS specific: VDC (Virtual Device Context)
1050
+ if (/^vdc\s+\w+/m.test(sampleText)) {
1051
+ return CiscoNXOSSchema;
1052
+ }
1053
+
1054
+ // Install feature-set (NX-OS)
1055
+ if (/^install\s+feature-set/m.test(sampleText)) {
1056
+ return CiscoNXOSSchema;
1057
+ }
1058
+
1059
+ // NX-OS specific: vrf context (vs IOS: ip vrf or vrf definition)
1060
+ if (/^vrf\s+context\s+\S+/m.test(sampleText)) {
1061
+ return CiscoNXOSSchema;
1062
+ }
1063
+
1064
+ // NX-OS specific: vpc domain
1065
+ if (/^vpc\s+domain\s+\d+/m.test(sampleText)) {
1066
+ return CiscoNXOSSchema;
1067
+ }
1068
+
1069
+ // ============ Arista EOS Detection ============
1070
+ // EOS is similar to IOS but has unique patterns
1071
+
1072
+ // MLAG configuration (most distinctive Arista feature)
1073
+ if (/^mlag\s+configuration/m.test(sampleText)) {
1074
+ return AristaEOSSchema;
1075
+ }
1076
+
1077
+ // Management API (eAPI) - Arista specific
1078
+ if (/^management\s+api\s+(http-commands|gnmi|netconf|restconf)/m.test(sampleText)) {
1079
+ return AristaEOSSchema;
1080
+ }
1081
+
1082
+ // Arista daemon configuration
1083
+ if (/^daemon\s+\S+/m.test(sampleText)) {
1084
+ return AristaEOSSchema;
1085
+ }
1086
+
1087
+ // Arista event-handler
1088
+ if (/^event-handler\s+\S+/m.test(sampleText)) {
1089
+ return AristaEOSSchema;
1090
+ }
1091
+
1092
+ // CVX (CloudVision Exchange) - Arista specific
1093
+ if (/^cvx$/m.test(sampleText) || /^management\s+cvx/m.test(sampleText)) {
1094
+ return AristaEOSSchema;
1095
+ }
1096
+
1097
+ // Arista VRF instance syntax (vs Cisco vrf definition or vrf context)
1098
+ if (/^vrf\s+instance\s+\S+/m.test(sampleText)) {
1099
+ return AristaEOSSchema;
1100
+ }
1101
+
1102
+ // Arista interface Vxlan (VXLAN VTEP interface)
1103
+ if (/^interface\s+Vxlan\d*/m.test(sampleText)) {
1104
+ return AristaEOSSchema;
1105
+ }
1106
+
1107
+ // Arista-style VXLAN flood vtep
1108
+ if (/^\s+vxlan\s+(vni|flood\s+vtep|source-interface)/m.test(sampleText)) {
1109
+ return AristaEOSSchema;
1110
+ }
1111
+
1112
+ // Arista peer-filter (BGP)
1113
+ if (/^peer-filter\s+\S+/m.test(sampleText)) {
1114
+ return AristaEOSSchema;
1115
+ }
1116
+
1117
+ // Arista tap aggregation
1118
+ if (/^tap\s+aggregation/m.test(sampleText)) {
1119
+ return AristaEOSSchema;
1120
+ }
1121
+
1122
+ // Arista queue-monitor
1123
+ if (/^queue-monitor\s+(streaming|length)/m.test(sampleText)) {
1124
+ return AristaEOSSchema;
1125
+ }
1126
+
1127
+ // Arista traffic-policy
1128
+ if (/^traffic-policy\s+\S+/m.test(sampleText)) {
1129
+ return AristaEOSSchema;
1130
+ }
1131
+
1132
+ // Arista hardware counter feature
1133
+ if (/^hardware\s+counter\s+feature/m.test(sampleText)) {
1134
+ return AristaEOSSchema;
1135
+ }
1136
+
1137
+ // Arista version string in show commands output or config
1138
+ if (/Arista\s+(DCS|vEOS|CCS)/m.test(sampleText)) {
1139
+ return AristaEOSSchema;
1140
+ }
1141
+
1142
+ // Arista EOS software version format
1143
+ if (/^!\s*Software\s+image\s+version:\s+\d+\.\d+\.\d+/m.test(sampleText)) {
1144
+ return AristaEOSSchema;
1145
+ }
1146
+
1147
+ // ============ Extreme Networks EXOS Detection ============
1148
+ // ExtremeXOS uses distinctive create/configure/enable command patterns
1149
+ // Must be checked before Cisco IOS as some patterns overlap
1150
+
1151
+ // EXOS distinctive "create vlan" command with named VLANs
1152
+ if (/^create\s+vlan\s+["']?\w+["']?\s*(tag\s+\d+)?/m.test(sampleText)) {
1153
+ return ExtremeEXOSSchema;
1154
+ }
1155
+
1156
+ // EXOS "configure vlan" commands (distinctive because VLANs are named)
1157
+ if (/^configure\s+vlan\s+["']?\w+["']?\s+(ipaddress|add\s+ports|tag)/m.test(sampleText)) {
1158
+ return ExtremeEXOSSchema;
1159
+ }
1160
+
1161
+ // EXOS SNMP sysname configuration (distinctive from Cisco hostname)
1162
+ if (/^configure\s+snmp\s+sysname\s+/m.test(sampleText)) {
1163
+ return ExtremeEXOSSchema;
1164
+ }
1165
+
1166
+ // EXOS "enable sharing" for LAG (EXOS-specific syntax)
1167
+ if (/^enable\s+sharing\s+\d+:\d+\s+grouping\s+/m.test(sampleText)) {
1168
+ return ExtremeEXOSSchema;
1169
+ }
1170
+
1171
+ // EXOS "create eaps" for Ethernet Automatic Protection Switching
1172
+ if (/^create\s+eaps\s+\S+/m.test(sampleText)) {
1173
+ return ExtremeEXOSSchema;
1174
+ }
1175
+
1176
+ // EXOS "configure sntp-client" (vs Cisco ntp server)
1177
+ if (/^configure\s+sntp-client\s+/m.test(sampleText)) {
1178
+ return ExtremeEXOSSchema;
1179
+ }
1180
+
1181
+ // EXOS "enable sntp-client" (distinctive EXOS enable pattern)
1182
+ if (/^enable\s+sntp-client/m.test(sampleText)) {
1183
+ return ExtremeEXOSSchema;
1184
+ }
1185
+
1186
+ // EXOS port format: slot:port (1:1, 2:24, etc.)
1187
+ // Combined with EXOS-specific commands
1188
+ if (/^configure\s+ports?\s+\d+:\d+/m.test(sampleText)) {
1189
+ return ExtremeEXOSSchema;
1190
+ }
1191
+
1192
+ // EXOS "enable jumbo-frame ports" (EXOS-specific)
1193
+ if (/^enable\s+jumbo-frame\s+ports\s+/m.test(sampleText)) {
1194
+ return ExtremeEXOSSchema;
1195
+ }
1196
+
1197
+ // EXOS "configure ip-mtu" (vs Cisco mtu)
1198
+ if (/^configure\s+ip-mtu\s+\d+\s+vlan\s+/m.test(sampleText)) {
1199
+ return ExtremeEXOSSchema;
1200
+ }
1201
+
1202
+ // EXOS Virtual Router (VR) configuration
1203
+ if (/^(create|configure)\s+vr\s+\S+/m.test(sampleText)) {
1204
+ return ExtremeEXOSSchema;
1205
+ }
1206
+
1207
+ // EXOS MLAG peer configuration
1208
+ if (/^(create|configure)\s+mlag\s+peer\s+/m.test(sampleText)) {
1209
+ return ExtremeEXOSSchema;
1210
+ }
1211
+
1212
+ // EXOS stacking configuration
1213
+ if (/^enable\s+stacking$/m.test(sampleText) || /^configure\s+stacking\s+node-address/m.test(sampleText)) {
1214
+ return ExtremeEXOSSchema;
1215
+ }
1216
+
1217
+ // EXOS ELRP (Extreme Loop Recovery Protocol)
1218
+ if (/^enable\s+elrp-client/m.test(sampleText)) {
1219
+ return ExtremeEXOSSchema;
1220
+ }
1221
+
1222
+ // EXOS distinctive "configure vlan Default delete ports"
1223
+ if (/^configure\s+vlan\s+Default\s+delete\s+ports/m.test(sampleText)) {
1224
+ return ExtremeEXOSSchema;
1225
+ }
1226
+
1227
+ // ============ Extreme Networks VOSS Detection ============
1228
+ // VOSS uses Cisco-like syntax but with distinctive VSP/Fabric Connect patterns
1229
+
1230
+ // VOSS "vlan create" command (vs EXOS "create vlan")
1231
+ if (/^vlan\s+create\s+\d+\s+type\s+(port-mstprstp|spbm-bvlan)/m.test(sampleText)) {
1232
+ return ExtremeVOSSSchema;
1233
+ }
1234
+
1235
+ // VOSS "vlan members" command (distinctive VOSS syntax)
1236
+ if (/^vlan\s+members\s+\d+\s+\d+\/\d+/m.test(sampleText)) {
1237
+ return ExtremeVOSSSchema;
1238
+ }
1239
+
1240
+ // VOSS "vlan i-sid" command (I-SID for SPBM/Fabric Connect)
1241
+ if (/^vlan\s+i-sid\s+\d+\s+\d+/m.test(sampleText)) {
1242
+ return ExtremeVOSSSchema;
1243
+ }
1244
+
1245
+ // VOSS "router isis" with SPBM configuration
1246
+ // SEC-002: Use safe line-by-line helper instead of dangerous [\s\S]*? regex
1247
+ if (hasVossRouterIsisSpbm(lines)) {
1248
+ return ExtremeVOSSSchema;
1249
+ }
1250
+
1251
+ // VOSS "spbm" command (Shortest Path Bridging MAC)
1252
+ if (/^spbm\s+\d+\s+(b-vid|nick-name|sys-id)/m.test(sampleText)) {
1253
+ return ExtremeVOSSSchema;
1254
+ }
1255
+
1256
+ // VOSS "i-sid" command (Instance Service ID)
1257
+ if (/^i-sid\s+\d+\s+(vlan|elan-transparent)/m.test(sampleText)) {
1258
+ return ExtremeVOSSSchema;
1259
+ }
1260
+
1261
+ // VOSS "interface GigabitEthernet" with slot/port format
1262
+ if (/^interface\s+GigabitEthernet\s+\d+\/\d+$/m.test(sampleText)) {
1263
+ return ExtremeVOSSSchema;
1264
+ }
1265
+
1266
+ // VOSS "interface mlt" (Multi-Link Trunk)
1267
+ if (/^interface\s+mlt\s+\d+/m.test(sampleText)) {
1268
+ return ExtremeVOSSSchema;
1269
+ }
1270
+
1271
+ // VOSS "mlt" command for MLT configuration
1272
+ if (/^mlt\s+\d+\s+(enable|name|member)/m.test(sampleText)) {
1273
+ return ExtremeVOSSSchema;
1274
+ }
1275
+
1276
+ // VOSS "lacp enable" under interface or MLT
1277
+ if (/^lacp\s+(enable|key|aggregation-wait-time)/m.test(sampleText)) {
1278
+ return ExtremeVOSSSchema;
1279
+ }
1280
+
1281
+ // VOSS "dvr" command (Distributed Virtual Routing)
1282
+ if (/^dvr\s+(leaf|controller|domain-id)/m.test(sampleText)) {
1283
+ return ExtremeVOSSSchema;
1284
+ }
1285
+
1286
+ // VOSS "cfm" command (Connectivity Fault Management)
1287
+ if (/^cfm\s+(spbm\s+mip|spbm\s+level|enable)/m.test(sampleText)) {
1288
+ return ExtremeVOSSSchema;
1289
+ }
1290
+
1291
+ // VOSS "snmp-server name" (vs Cisco hostname)
1292
+ if (/^snmp-server\s+name\s+["']?\S+["']?/m.test(sampleText)) {
1293
+ return ExtremeVOSSSchema;
1294
+ }
1295
+
1296
+ // VOSS "boot config flags" command
1297
+ if (/^boot\s+config\s+flags\s+/m.test(sampleText)) {
1298
+ return ExtremeVOSSSchema;
1299
+ }
1300
+
1301
+ // VOSS "sys name" command
1302
+ if (/^sys\s+name\s+["']?\S+["']?/m.test(sampleText)) {
1303
+ return ExtremeVOSSSchema;
1304
+ }
1305
+
1306
+ // ============ Nokia SR OS Detection ============
1307
+ // SR OS uses hierarchical CLI with configure/router/system blocks
1308
+ // and distinctive port notation (slot/mda/port) and admin-state commands
1309
+ // Must be checked before Huawei and Cisco as some patterns overlap
1310
+
1311
+ // Nokia SR OS distinctive "configure" followed by "router" block structure
1312
+ if (/^configure$/m.test(sampleText) && /^\s+router\s+"?[^"]*"?\s*$/m.test(sampleText)) {
1313
+ return NokiaSROSSchema;
1314
+ }
1315
+
1316
+ // Nokia SR OS distinctive port notation with admin-state
1317
+ // Port format: port X/Y/Z (slot/mda/port)
1318
+ // SEC-002: Use safe line-by-line helper instead of dangerous [\s\S]*? regex
1319
+ if (hasNokiaPortAdminState(lines)) {
1320
+ return NokiaSROSSchema;
1321
+ }
1322
+
1323
+ // Nokia SR OS router with named interfaces using quotes
1324
+ // SEC-002: Use safe line-by-line helper instead of dangerous [\s\S]*? regex
1325
+ if (hasNokiaRouterInterfaceBlock(lines)) {
1326
+ return NokiaSROSSchema;
1327
+ }
1328
+
1329
+ // Nokia SR OS system name configuration (system > name "...")
1330
+ // SEC-002: Use safe line-by-line helper instead of dangerous [\s\S]*? regex
1331
+ if (hasNokiaSystemNameBlock(lines)) {
1332
+ return NokiaSROSSchema;
1333
+ }
1334
+
1335
+ // Nokia SR OS service types (vpls, vprn, epipe, ies)
1336
+ if (/^\s+(vpls|vprn|epipe|ies)\s+\d+\s+(name|customer)/m.test(sampleText)) {
1337
+ return NokiaSROSSchema;
1338
+ }
1339
+
1340
+ // Nokia SR OS SAP (Service Access Point) configuration
1341
+ if (/^\s+sap\s+\d+\/\d+\/\d+:\d+/m.test(sampleText)) {
1342
+ return NokiaSROSSchema;
1343
+ }
1344
+
1345
+ // Nokia SR OS MPLS LSP configuration
1346
+ if (/^\s+lsp\s+"[^"]+"\s*$/m.test(sampleText)) {
1347
+ return NokiaSROSSchema;
1348
+ }
1349
+
1350
+ // Nokia SR OS echo command for comments
1351
+ if (/^echo\s+"[^"]*"$/m.test(sampleText)) {
1352
+ return NokiaSROSSchema;
1353
+ }
1354
+
1355
+ // Nokia SR OS admin-state enable/disable pattern (very distinctive)
1356
+ if (/^\s+admin-state\s+(enable|disable)/m.test(sampleText)) {
1357
+ return NokiaSROSSchema;
1358
+ }
1359
+
1360
+ // Nokia SR OS policy-options block
1361
+ if (/^policy-options$/m.test(sampleText) && /^\s+policy-statement\s+"[^"]+"/m.test(sampleText)) {
1362
+ return NokiaSROSSchema;
1363
+ }
1364
+
1365
+ // Nokia SR OS filter configuration
1366
+ if (/^filter$/m.test(sampleText) && /^\s+ip-filter\s+\d+/m.test(sampleText)) {
1367
+ return NokiaSROSSchema;
1368
+ }
1369
+
1370
+ // Nokia SR OS exit all pattern (distinctive from other vendors)
1371
+ if (/^exit\s+all$/m.test(sampleText)) {
1372
+ return NokiaSROSSchema;
1373
+ }
1374
+
1375
+ // Nokia SR OS card and MDA configuration
1376
+ // SEC-002: Use safe line-by-line helper instead of dangerous [\s\S]*? regex
1377
+ if (hasNokiaCardMdaBlock(lines)) {
1378
+ return NokiaSROSSchema;
1379
+ }
1380
+
1381
+ // Nokia SR OS log configuration
1382
+ if (/^log$/m.test(sampleText) && /^\s+(log-id|syslog|snmp-trap-group)\s+\d+/m.test(sampleText)) {
1383
+ return NokiaSROSSchema;
1384
+ }
1385
+
1386
+ // Nokia SR OS BGP with group and neighbor using quoted names
1387
+ // SEC-002: Use safe line-by-line helper instead of dangerous [\s\S]*? regex
1388
+ if (hasNokiaBgpGroupBlock(lines)) {
1389
+ return NokiaSROSSchema;
1390
+ }
1391
+
1392
+ // ============ Huawei VRP Detection ============
1393
+ // VRP uses distinctive sysname, interface naming, and undo commands
1394
+ // Must be checked before Cisco IOS as some patterns overlap
1395
+
1396
+ // Huawei sysname command (most distinctive - vs Cisco's hostname)
1397
+ if (/^sysname\s+\S+/m.test(sampleText)) {
1398
+ return HuaweiVRPSchema;
1399
+ }
1400
+
1401
+ // Huawei VRP version/header comment
1402
+ if (/^#\s*(HuaWei|huawei|Huawei)/m.test(sampleText)) {
1403
+ return HuaweiVRPSchema;
1404
+ }
1405
+
1406
+ // Huawei interface naming: GigabitEthernet X/Y/Z format (slot/card/port)
1407
+ // Note: Must be before Cisco detection since Cisco also uses GigabitEthernet
1408
+ // Huawei uses space before numbers: "GigabitEthernet 0/0/1" vs Cisco "GigabitEthernet0/0/1"
1409
+ if (/^interface\s+GigabitEthernet\s+\d+\/\d+\/\d+/m.test(sampleText)) {
1410
+ return HuaweiVRPSchema;
1411
+ }
1412
+
1413
+ // Huawei high-speed interface naming: XGigabitEthernet, 40GE, 100GE
1414
+ if (/^interface\s+(XGigabitEthernet|40GE|100GE|25GE|10GE|Eth-Trunk)\s*/m.test(sampleText)) {
1415
+ return HuaweiVRPSchema;
1416
+ }
1417
+
1418
+ // Huawei Vlanif interface (vs Cisco's Vlan or interface Vlan)
1419
+ if (/^interface\s+Vlanif\s*\d+/m.test(sampleText)) {
1420
+ return HuaweiVRPSchema;
1421
+ }
1422
+
1423
+ // Huawei VRP undo command (negation - very distinctive)
1424
+ if (/^\s*undo\s+(info-center|shutdown|portswitch|stp|lldp|ntdp)/m.test(sampleText)) {
1425
+ return HuaweiVRPSchema;
1426
+ }
1427
+
1428
+ // Huawei protocol configuration with process ID directly after keyword
1429
+ // ospf 1, bgp 65000, isis 1 (vs Cisco's router ospf 1, router bgp 65000)
1430
+ if (/^ospf\s+\d+\s*$/m.test(sampleText) || /^bgp\s+\d+\s*$/m.test(sampleText)) {
1431
+ return HuaweiVRPSchema;
1432
+ }
1433
+
1434
+ // Huawei isis configuration
1435
+ if (/^isis\s+\d+\s*$/m.test(sampleText)) {
1436
+ return HuaweiVRPSchema;
1437
+ }
1438
+
1439
+ // Huawei AAA configuration block
1440
+ // SEC-002: Use safe line-by-line helper instead of dangerous combined regex
1441
+ if (hasHuaweiAaaBlock(lines)) {
1442
+ return HuaweiVRPSchema;
1443
+ }
1444
+
1445
+ // Huawei user-interface configuration (vs Cisco's line vty)
1446
+ if (/^user-interface\s+(vty|console|current)\s*/m.test(sampleText)) {
1447
+ return HuaweiVRPSchema;
1448
+ }
1449
+
1450
+ // Huawei local-user configuration
1451
+ if (/^local-user\s+\S+\s+(password|privilege|service-type)/m.test(sampleText)) {
1452
+ return HuaweiVRPSchema;
1453
+ }
1454
+
1455
+ // Huawei VPN instance (vs Cisco's ip vrf or vrf definition)
1456
+ if (/^ip\s+vpn-instance\s+\S+/m.test(sampleText)) {
1457
+ return HuaweiVRPSchema;
1458
+ }
1459
+
1460
+ // Huawei HWTACACS (Huawei's TACACS implementation)
1461
+ if (/^hwtacacs-server\s+(template|shared-key)/m.test(sampleText)) {
1462
+ return HuaweiVRPSchema;
1463
+ }
1464
+
1465
+ // Huawei info-center (logging configuration)
1466
+ if (/^info-center\s+(enable|source|loghost)/m.test(sampleText)) {
1467
+ return HuaweiVRPSchema;
1468
+ }
1469
+
1470
+ // Huawei drop-profile or queue-profile
1471
+ if (/^(drop-profile|qos\s+queue-profile)\s+\S+/m.test(sampleText)) {
1472
+ return HuaweiVRPSchema;
1473
+ }
1474
+
1475
+ // Huawei port link-type in interface context
1476
+ if (/^\s*port\s+link-type\s+(access|trunk|hybrid)/m.test(sampleText)) {
1477
+ return HuaweiVRPSchema;
1478
+ }
1479
+
1480
+ // Huawei port default vlan (vs Cisco switchport access vlan)
1481
+ if (/^\s*port\s+default\s+vlan\s+\d+/m.test(sampleText)) {
1482
+ return HuaweiVRPSchema;
1483
+ }
1484
+
1485
+ // Huawei port trunk allow-pass vlan
1486
+ if (/^\s*port\s+trunk\s+allow-pass\s+vlan\s+/m.test(sampleText)) {
1487
+ return HuaweiVRPSchema;
1488
+ }
1489
+
1490
+ // Huawei display commands in comments or header
1491
+ if (/^#\s*display\s+current-configuration/m.test(sampleText)) {
1492
+ return HuaweiVRPSchema;
1493
+ }
1494
+
1495
+ // Huawei return command (exits to user view)
1496
+ if (/^return\s*$/m.test(sampleText)) {
1497
+ return HuaweiVRPSchema;
1498
+ }
1499
+
1500
+ // ============ Default: Cisco IOS ============
1501
+ // Most common format, used as fallback
1502
+ return CiscoIOSSchema;
1503
+ }
1504
+
1505
+ // Re-export all vendor schemas for direct access
1506
+ export { CiscoIOSSchema } from './cisco-ios';
1507
+ export { CiscoNXOSSchema } from './cisco-nxos';
1508
+ export { JuniperJunOSSchema } from './juniper-junos';
1509
+ export { ArubaAOSCXSchema } from './aruba-aoscx';
1510
+ export { ArubaAOSSwitchSchema } from './aruba-aosswitch';
1511
+ export { ArubaWLCSchema } from './aruba-wlc';
1512
+ export { PaloAltoPANOSSchema } from './paloalto-panos';
1513
+ export { AristaEOSSchema } from './arista-eos';
1514
+ export { VyOSSchema } from './vyos-vyos';
1515
+ export { FortinetFortiGateSchema } from './fortinet-fortigate';
1516
+ export { ExtremeEXOSSchema } from './extreme-exos';
1517
+ export { ExtremeVOSSSchema } from './extreme-voss';
1518
+ export { HuaweiVRPSchema } from './huawei-vrp';
1519
+ export { MikroTikRouterOSSchema } from './mikrotik-routeros';
1520
+ export { NokiaSROSSchema } from './nokia-sros';
1521
+ export { CumulusLinuxSchema } from './cumulus-linux';