@sentriflow/cli 0.1.2 → 0.1.4

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 (4) hide show
  1. package/LICENSE +0 -0
  2. package/README.md +0 -0
  3. package/dist/index.js +160 -148
  4. package/package.json +54 -54
package/LICENSE CHANGED
File without changes
package/README.md CHANGED
File without changes
package/dist/index.js CHANGED
@@ -10407,7 +10407,7 @@ function generateSarif(results, filePath, rules, options = {}) {
10407
10407
  tool: {
10408
10408
  driver: {
10409
10409
  name: "Sentriflow",
10410
- version: "0.1.2",
10410
+ version: "0.1.4",
10411
10411
  informationUri: "https://github.com/sentriflow/sentriflow",
10412
10412
  rules: sarifRules,
10413
10413
  // SEC-007: Include CWE taxonomy when rules reference it
@@ -10513,7 +10513,7 @@ function generateMultiFileSarif(fileResults, rules, options = {}) {
10513
10513
  tool: {
10514
10514
  driver: {
10515
10515
  name: "Sentriflow",
10516
- version: "0.1.2",
10516
+ version: "0.1.4",
10517
10517
  informationUri: "https://github.com/sentriflow/sentriflow",
10518
10518
  rules: sarifRules,
10519
10519
  // SEC-007: Include CWE taxonomy when rules reference it
@@ -10740,86 +10740,9 @@ var InterfaceDescriptionRequired = {
10740
10740
  };
10741
10741
  }
10742
10742
  };
10743
- var NoPlaintextPasswords = {
10744
- id: "NET-SEC-001",
10745
- selector: "password",
10746
- vendor: "common",
10747
- metadata: {
10748
- level: "error",
10749
- obu: "Security",
10750
- owner: "SecOps",
10751
- remediation: 'Use "secret" instead of "password", or ensure password is encrypted (type 7 or higher).'
10752
- },
10753
- check: (node) => {
10754
- const params = node.params;
10755
- const nodeId = node.id;
10756
- if (includesIgnoreCase(nodeId, "encryption") || includesIgnoreCase(nodeId, "service")) {
10757
- return {
10758
- passed: true,
10759
- message: "Global password configuration command.",
10760
- ruleId: "NET-SEC-001",
10761
- nodeId: node.id,
10762
- level: "info",
10763
- loc: node.loc
10764
- };
10765
- }
10766
- if (params.length >= 2) {
10767
- const typeOrValue = params[1];
10768
- if (!typeOrValue) {
10769
- return {
10770
- passed: false,
10771
- message: 'Possible plaintext password detected. Use encryption type 7 or "secret" command.',
10772
- ruleId: "NET-SEC-001",
10773
- nodeId: node.id,
10774
- level: "error",
10775
- loc: node.loc
10776
- };
10777
- }
10778
- if (typeOrValue === "7" || typeOrValue === "5" || typeOrValue === "8" || typeOrValue === "9") {
10779
- return {
10780
- passed: true,
10781
- message: "Password is encrypted.",
10782
- ruleId: "NET-SEC-001",
10783
- nodeId: node.id,
10784
- level: "info",
10785
- loc: node.loc
10786
- };
10787
- }
10788
- if (typeOrValue === "0") {
10789
- return {
10790
- passed: false,
10791
- message: "Plaintext password detected (type 0).",
10792
- ruleId: "NET-SEC-001",
10793
- nodeId: node.id,
10794
- level: "error",
10795
- loc: node.loc
10796
- };
10797
- }
10798
- if (!/^\d+$/.test(typeOrValue)) {
10799
- return {
10800
- passed: false,
10801
- message: 'Possible plaintext password detected. Use encryption type 7 or "secret" command.',
10802
- ruleId: "NET-SEC-001",
10803
- nodeId: node.id,
10804
- level: "error",
10805
- loc: node.loc
10806
- };
10807
- }
10808
- }
10809
- return {
10810
- passed: true,
10811
- message: "Password check passed.",
10812
- ruleId: "NET-SEC-001",
10813
- nodeId: node.id,
10814
- level: "info",
10815
- loc: node.loc
10816
- };
10817
- }
10818
- };
10819
10743
  var allCommonRules = [
10820
10744
  NoMulticastBroadcastIp,
10821
- InterfaceDescriptionRequired,
10822
- NoPlaintextPasswords
10745
+ InterfaceDescriptionRequired
10823
10746
  ];
10824
10747
 
10825
10748
  // ../rules-default/src/cisco/ios-rules.ts
@@ -10921,12 +10844,89 @@ var EnableSecretStrong = {
10921
10844
  return { passed: true, message: "Enable secret check passed.", ruleId: "NET-AAA-003", nodeId: node.id, level: "info", loc: node.loc };
10922
10845
  }
10923
10846
  };
10847
+ var CiscoNoPlaintextPasswords = {
10848
+ id: "NET-SEC-001",
10849
+ selector: "password",
10850
+ vendor: ["cisco-ios", "cisco-nxos"],
10851
+ metadata: {
10852
+ level: "error",
10853
+ obu: "Security",
10854
+ owner: "SecOps",
10855
+ remediation: 'Use "secret" instead of "password", or ensure password is encrypted (type 7 or higher).'
10856
+ },
10857
+ check: (node) => {
10858
+ const params = node.params;
10859
+ const nodeId = node.id;
10860
+ if (includesIgnoreCase(nodeId, "encryption") || includesIgnoreCase(nodeId, "service")) {
10861
+ return {
10862
+ passed: true,
10863
+ message: "Global password configuration command.",
10864
+ ruleId: "NET-SEC-001",
10865
+ nodeId: node.id,
10866
+ level: "info",
10867
+ loc: node.loc
10868
+ };
10869
+ }
10870
+ if (params.length >= 2) {
10871
+ const typeOrValue = params[1];
10872
+ if (!typeOrValue) {
10873
+ return {
10874
+ passed: false,
10875
+ message: 'Possible plaintext password detected. Use encryption type 7 or "secret" command.',
10876
+ ruleId: "NET-SEC-001",
10877
+ nodeId: node.id,
10878
+ level: "error",
10879
+ loc: node.loc
10880
+ };
10881
+ }
10882
+ if (typeOrValue === "7" || typeOrValue === "5" || typeOrValue === "8" || typeOrValue === "9") {
10883
+ return {
10884
+ passed: true,
10885
+ message: "Password is encrypted.",
10886
+ ruleId: "NET-SEC-001",
10887
+ nodeId: node.id,
10888
+ level: "info",
10889
+ loc: node.loc
10890
+ };
10891
+ }
10892
+ if (typeOrValue === "0") {
10893
+ return {
10894
+ passed: false,
10895
+ message: "Plaintext password detected (type 0).",
10896
+ ruleId: "NET-SEC-001",
10897
+ nodeId: node.id,
10898
+ level: "error",
10899
+ loc: node.loc
10900
+ };
10901
+ }
10902
+ if (!/^\d+$/.test(typeOrValue)) {
10903
+ return {
10904
+ passed: false,
10905
+ message: 'Possible plaintext password detected. Use encryption type 7 or "secret" command.',
10906
+ ruleId: "NET-SEC-001",
10907
+ nodeId: node.id,
10908
+ level: "error",
10909
+ loc: node.loc
10910
+ };
10911
+ }
10912
+ }
10913
+ return {
10914
+ passed: true,
10915
+ message: "Password check passed.",
10916
+ ruleId: "NET-SEC-001",
10917
+ nodeId: node.id,
10918
+ level: "info",
10919
+ loc: node.loc
10920
+ };
10921
+ }
10922
+ };
10924
10923
  var allCiscoRules = [
10925
10924
  // Layer 2 Trunk
10926
10925
  TrunkNoDTP,
10927
10926
  // Layer 2 Access
10928
10927
  AccessExplicitMode,
10929
- // Service Hardening
10928
+ // Security
10929
+ CiscoNoPlaintextPasswords,
10930
10930
  EnableSecretStrong
10931
10931
  ];
10932
10932
 
@@ -11759,6 +11759,40 @@ var VyosHostnameRequired = {
11759
11759
  };
11760
11760
  }
11761
11761
  };
11762
+ var VyosNoPlaintextPassword = {
11763
+ id: "VYOS-SEC-001",
11764
+ selector: "authentication",
11765
+ vendor: "vyos",
11766
+ metadata: {
11767
+ level: "error",
11768
+ obu: "Security",
11769
+ owner: "SecOps",
11770
+ remediation: 'Use "encrypted-password" with a pre-hashed password, or let VyOS hash it during configuration.'
11771
+ },
11772
+ check: (node) => {
11773
+ const hasPlaintext = node.children.some(
11774
+ (child) => startsWithIgnoreCase(child.id, "plaintext-password")
11775
+ );
11776
+ if (hasPlaintext) {
11777
+ return {
11778
+ passed: false,
11779
+ message: "Plaintext password found in configuration. VyOS should store hashed passwords only.",
11780
+ ruleId: "VYOS-SEC-001",
11781
+ nodeId: node.id,
11782
+ level: "error",
11783
+ loc: node.loc
11784
+ };
11785
+ }
11786
+ return {
11787
+ passed: true,
11788
+ message: "No plaintext passwords in configuration.",
11789
+ ruleId: "VYOS-SEC-001",
11790
+ nodeId: node.id,
11791
+ level: "info",
11792
+ loc: node.loc
11793
+ };
11794
+ }
11795
+ };
11762
11796
  var VyosInterfaceDescription = {
11763
11797
  id: "VYOS-IF-001",
11764
11798
  selector: "interfaces",
@@ -11855,6 +11889,8 @@ var VyosFirewallDefaultAction = {
11855
11889
  var allVyosRules = [
11856
11890
  // System
11857
11891
  VyosHostnameRequired,
11892
+ // Security
11893
+ VyosNoPlaintextPassword,
11858
11894
  // Firewall
11859
11895
  VyosFirewallDefaultAction,
11860
11896
  // Interfaces
@@ -13093,55 +13129,6 @@ var common_json_rules_default = {
13093
13129
  ]
13094
13130
  },
13095
13131
  failureMessage: "Interface {nodeId} is missing documentation (description)"
13096
- },
13097
- {
13098
- id: "JSON-COMMON-002",
13099
- selector: "interface",
13100
- vendor: "common",
13101
- metadata: {
13102
- level: "warning",
13103
- obu: "Network Engineering",
13104
- owner: "NetOps",
13105
- description: "Shutdown interfaces should have a description explaining why",
13106
- remediation: "Add a description to shutdown interfaces documenting the reason"
13107
- },
13108
- check: {
13109
- type: "and",
13110
- conditions: [
13111
- {
13112
- type: "helper",
13113
- helper: "isInterfaceDefinition",
13114
- args: [{ $ref: "node" }]
13115
- },
13116
- {
13117
- type: "helper",
13118
- helper: "isShutdown",
13119
- args: [{ $ref: "node" }]
13120
- },
13121
- {
13122
- type: "child_not_exists",
13123
- selector: "description"
13124
- }
13125
- ]
13126
- },
13127
- failureMessage: "Shutdown interface {nodeId} should have a description explaining why it's disabled"
13128
- },
13129
- {
13130
- id: "JSON-COMMON-003",
13131
- selector: "ntp",
13132
- vendor: "common",
13133
- metadata: {
13134
- level: "warning",
13135
- obu: "Operations",
13136
- owner: "SysOps",
13137
- description: "NTP should be configured for time synchronization",
13138
- remediation: "Configure NTP servers for accurate time synchronization"
13139
- },
13140
- check: {
13141
- type: "child_not_exists",
13142
- selector: "server"
13143
- },
13144
- failureMessage: "NTP configuration is missing server entries"
13145
13132
  }
13146
13133
  ]
13147
13134
  };
@@ -13307,6 +13294,16 @@ var allJsonRules = [
13307
13294
  ...commonJsonRules,
13308
13295
  ...juniperJsonRules
13309
13296
  ];
13297
+ function getJsonRulesByVendor(vendorId) {
13298
+ return allJsonRules.filter((rule) => {
13299
+ if (!rule.vendor) return true;
13300
+ if (rule.vendor === "common") return true;
13301
+ if (Array.isArray(rule.vendor)) {
13302
+ return rule.vendor.includes(vendorId) || rule.vendor.includes("common");
13303
+ }
13304
+ return rule.vendor === vendorId;
13305
+ });
13306
+ }
13310
13307
 
13311
13308
  // ../rules-default/src/index.ts
13312
13309
  var allRules = [
@@ -13341,25 +13338,25 @@ var allRules = [
13341
13338
  ];
13342
13339
  var vendorRulesRegistry = {
13343
13340
  // Cisco platforms share the same rules
13344
- "cisco-ios": () => [...allCommonRules, ...allCiscoRules],
13345
- "cisco-nxos": () => [...allCommonRules, ...allCiscoRules],
13341
+ "cisco-ios": () => [...allCommonRules, ...allCiscoRules, ...getJsonRulesByVendor("cisco-ios")],
13342
+ "cisco-nxos": () => [...allCommonRules, ...allCiscoRules, ...getJsonRulesByVendor("cisco-nxos")],
13346
13343
  // Juniper
13347
- "juniper-junos": () => [...allCommonRules, ...allJuniperRules],
13344
+ "juniper-junos": () => [...allCommonRules, ...allJuniperRules, ...getJsonRulesByVendor("juniper-junos")],
13348
13345
  // Aruba platforms have variant-specific rules
13349
- "aruba-aoscx": () => getRulesByArubaVendor("aruba-aoscx"),
13350
- "aruba-aosswitch": () => getRulesByArubaVendor("aruba-aosswitch"),
13351
- "aruba-wlc": () => getRulesByArubaVendor("aruba-wlc"),
13346
+ "aruba-aoscx": () => [...getRulesByArubaVendor("aruba-aoscx"), ...getJsonRulesByVendor("aruba-aoscx")],
13347
+ "aruba-aosswitch": () => [...getRulesByArubaVendor("aruba-aosswitch"), ...getJsonRulesByVendor("aruba-aosswitch")],
13348
+ "aruba-wlc": () => [...getRulesByArubaVendor("aruba-wlc"), ...getJsonRulesByVendor("aruba-wlc")],
13352
13349
  // Other vendors
13353
- "paloalto-panos": () => getRulesByPaloAltoVendor(),
13354
- "arista-eos": () => getRulesByAristaVendor(),
13355
- "vyos": () => getRulesByVyosVendor(),
13356
- "fortinet-fortigate": () => getRulesByFortinetVendor(),
13357
- "extreme-exos": () => getRulesByExtremeVendor("extreme-exos"),
13358
- "extreme-voss": () => getRulesByExtremeVendor("extreme-voss"),
13359
- "huawei-vrp": () => getRulesByHuaweiVendor(),
13360
- "mikrotik-routeros": () => getRulesByMikroTikVendor(),
13361
- "nokia-sros": () => getRulesByNokiaVendor(),
13362
- "cumulus-linux": () => getRulesByCumulusVendor()
13350
+ "paloalto-panos": () => [...getRulesByPaloAltoVendor(), ...getJsonRulesByVendor("paloalto-panos")],
13351
+ "arista-eos": () => [...getRulesByAristaVendor(), ...getJsonRulesByVendor("arista-eos")],
13352
+ "vyos": () => [...getRulesByVyosVendor(), ...getJsonRulesByVendor("vyos")],
13353
+ "fortinet-fortigate": () => [...getRulesByFortinetVendor(), ...getJsonRulesByVendor("fortinet-fortigate")],
13354
+ "extreme-exos": () => [...getRulesByExtremeVendor("extreme-exos"), ...getJsonRulesByVendor("extreme-exos")],
13355
+ "extreme-voss": () => [...getRulesByExtremeVendor("extreme-voss"), ...getJsonRulesByVendor("extreme-voss")],
13356
+ "huawei-vrp": () => [...getRulesByHuaweiVendor(), ...getJsonRulesByVendor("huawei-vrp")],
13357
+ "mikrotik-routeros": () => [...getRulesByMikroTikVendor(), ...getJsonRulesByVendor("mikrotik-routeros")],
13358
+ "nokia-sros": () => [...getRulesByNokiaVendor(), ...getJsonRulesByVendor("nokia-sros")],
13359
+ "cumulus-linux": () => [...getRulesByCumulusVendor(), ...getJsonRulesByVendor("cumulus-linux")]
13363
13360
  };
13364
13361
  function getRulesByVendor(vendorId) {
13365
13362
  const getRules = vendorRulesRegistry[vendorId];
@@ -14194,7 +14191,7 @@ function validateDirectoryPath(dirPath, allowedBaseDirs) {
14194
14191
 
14195
14192
  // index.ts
14196
14193
  var program = new Command();
14197
- program.name("sentriflow").description("SentriFlow Network Configuration Validator").version("0.1.2").argument("[file]", "Path to the configuration file").option("--ast", "Output the AST instead of rule results").option("-f, --format <format>", "Output format (json, sarif)", "json").option("-q, --quiet", "Only output failures (suppress passed results)").option("-c, --config <path>", "Path to config file (default: auto-detect)").option("--no-config", "Ignore config file").option("-r, --rules <path>", "Additional rules file to load (legacy)").option("-p, --rule-pack <path>", "Rule pack file to load").option(
14194
+ program.name("sentriflow").description("SentriFlow Network Configuration Validator").version("0.1.4").argument("[file]", "Path to the configuration file").option("--ast", "Output the AST instead of rule results").option("-f, --format <format>", "Output format (json, sarif)", "json").option("-q, --quiet", "Only output failures (suppress passed results)").option("-c, --config <path>", "Path to config file (default: auto-detect)").option("--no-config", "Ignore config file").option("-r, --rules <path>", "Additional rules file to load (legacy)").option("-p, --rule-pack <path>", "Rule pack file to load").option(
14198
14195
  "--encrypted-pack <path...>",
14199
14196
  "SEC-012: Path(s) to encrypted rule pack(s) (.grpx), can specify multiple"
14200
14197
  ).option(
@@ -14601,4 +14598,19 @@ Scan complete: ${allFileResults.length} files, ${totalFailures} failures, ${tota
14601
14598
  process.exit(2);
14602
14599
  }
14603
14600
  });
14604
- program.parse();
14601
+ async function loadLicensingExtension() {
14602
+ try {
14603
+ const licensingModulePath = "@sentriflow/licensing/cli";
14604
+ const licensing = await import(
14605
+ /* @vite-ignore */
14606
+ licensingModulePath
14607
+ );
14608
+ if (licensing.registerCommands) {
14609
+ licensing.registerCommands(program);
14610
+ }
14611
+ } catch {
14612
+ }
14613
+ }
14614
+ loadLicensingExtension().finally(() => {
14615
+ program.parse();
14616
+ });
package/package.json CHANGED
@@ -1,54 +1,54 @@
1
- {
2
- "name": "@sentriflow/cli",
3
- "version": "0.1.2",
4
- "description": "SentriFlow CLI - Network configuration linter and validator",
5
- "license": "Apache-2.0",
6
- "main": "dist/index.js",
7
- "module": "dist/index.js",
8
- "type": "module",
9
- "repository": {
10
- "type": "git",
11
- "url": "git+https://github.com/sentriflow/sentriflow.git",
12
- "directory": "packages/cli"
13
- },
14
- "homepage": "https://github.com/sentriflow/sentriflow#readme",
15
- "bugs": {
16
- "url": "https://github.com/sentriflow/sentriflow/issues"
17
- },
18
- "keywords": [
19
- "cli",
20
- "network",
21
- "configuration",
22
- "linter",
23
- "security",
24
- "compliance",
25
- "sarif"
26
- ],
27
- "files": [
28
- "dist",
29
- "LICENSE",
30
- "README.md"
31
- ],
32
- "publishConfig": {
33
- "access": "public"
34
- },
35
- "scripts": {
36
- "build": "node build.mjs",
37
- "prepublishOnly": "bun run build"
38
- },
39
- "dependencies": {
40
- "commander": "^14.0.2"
41
- },
42
- "devDependencies": {
43
- "@sentriflow/core": "workspace:*",
44
- "@sentriflow/rules-default": "workspace:*",
45
- "bun-types": "latest",
46
- "esbuild": "^0.27.0"
47
- },
48
- "bin": {
49
- "sentriflow": "dist/index.js"
50
- },
51
- "engines": {
52
- "node": ">=18.0.0"
53
- }
54
- }
1
+ {
2
+ "name": "@sentriflow/cli",
3
+ "version": "0.1.4",
4
+ "description": "SentriFlow CLI - Network configuration linter and validator",
5
+ "license": "Apache-2.0",
6
+ "main": "dist/index.js",
7
+ "module": "dist/index.js",
8
+ "type": "module",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/sentriflow/sentriflow.git",
12
+ "directory": "packages/cli"
13
+ },
14
+ "homepage": "https://github.com/sentriflow/sentriflow#readme",
15
+ "bugs": {
16
+ "url": "https://github.com/sentriflow/sentriflow/issues"
17
+ },
18
+ "keywords": [
19
+ "cli",
20
+ "network",
21
+ "configuration",
22
+ "linter",
23
+ "security",
24
+ "compliance",
25
+ "sarif"
26
+ ],
27
+ "files": [
28
+ "dist",
29
+ "LICENSE",
30
+ "README.md"
31
+ ],
32
+ "publishConfig": {
33
+ "access": "public"
34
+ },
35
+ "scripts": {
36
+ "build": "bun build.mjs",
37
+ "prepublishOnly": "bun run build"
38
+ },
39
+ "dependencies": {
40
+ "commander": "^14.0.2"
41
+ },
42
+ "devDependencies": {
43
+ "@sentriflow/core": "workspace:*",
44
+ "@sentriflow/rules-default": "workspace:*",
45
+ "bun-types": "latest",
46
+ "esbuild": "^0.27.0"
47
+ },
48
+ "bin": {
49
+ "sentriflow": "dist/index.js"
50
+ },
51
+ "engines": {
52
+ "node": ">=18.0.0"
53
+ }
54
+ }